floatable-rails 0.1.5 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c125142ad80b216bb4877b12f84b9cbed92d61225c13486ea3468aca4247f72
4
- data.tar.gz: 8693d60951dc3bb1631a1399f3bab47d9a2758d989cdf950542f738a9fe46053
3
+ metadata.gz: e24ac3ac7be11f6b3ef371aa01f178361fb4eac3a3826a324f3774ca56005ad0
4
+ data.tar.gz: 9b07e10f8906475a817797eecc6cb961fe90b9938a47f46cfeaf860d03acf005
5
5
  SHA512:
6
- metadata.gz: f4a76a6b78be41abf4f1ac7cf5d889aea041486560bfe0ae394197c2c3761b439239c01ddf4a3350a8952586d41fcde1739c23dd5732a22b4a4e789cc6502c56
7
- data.tar.gz: 7058a3f4efa223046b51057393ae68c5ed0f69fed2070fff360b7ed266cfd74fd4ebf57a1df2df3322bb9b7cb517f343209ebfdf1aa35f95c1565fa3571aaf60
6
+ metadata.gz: 97fb690d9c0081d42401fb42dc5189a281b123a925168563e72a6baf1d2e92d1dc05f3302c8b8e5f8d89b9a3eb7a7a1573916b8279a0271e122186eda60c38c3
7
+ data.tar.gz: 3cd295707a169159a79cb1b576249115c4b2fbd696c5abf473369f027984965e0702d1a6e8ab9118cd844ee3c040b69be20584d4f222c0a1c7b2fb09c29efdf1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.6
4
+
5
+ - Simplify instrumentation to path-only metadata (`data-floatable-path`).
6
+ - Remove tag instrumentation and rely on view marker + middleware assignment.
7
+ - Harden fail-open behavior so Floatable errors are logged and do not break host app requests.
8
+
3
9
  ## 0.1.5
4
10
 
5
11
  - Preserve Vue-style directive/event attributes by using the HTML5 parser when available.
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Floatable::Rails
2
2
 
3
3
  Rails integration for Floatable Toolbar. It instruments HTML output with
4
- metadata and injects the Floatable script so your UI can be inspected and
4
+ `data-floatable-path` attributes and injects the Floatable script so your UI can be inspected and
5
5
  annotated in the browser.
6
6
 
7
7
  ## Usage
@@ -1,6 +1,5 @@
1
1
  require "rails"
2
2
  require "floatable/rails/helpers"
3
- require "floatable/rails/tag_instrumentation"
4
3
  require "floatable/rails/erb_view_instrumentation"
5
4
  require "floatable/rails/slim_view_instrumentation"
6
5
  require "floatable/rails/response_middleware"
@@ -9,37 +8,82 @@ module Floatable
9
8
  module Rails
10
9
  class Engine < ::Rails::Engine
11
10
  isolate_namespace Floatable::Rails
11
+ class << self
12
+ def with_fail_open_init(name)
13
+ yield
14
+ rescue => e
15
+ ::Floatable::Rails.log_error("[Floatable::Rails::Engine] #{name} init failed: #{e.class} - #{e.message}")
16
+ end
17
+
18
+ def apply_erb_patch!
19
+ handler = ::ActionView::Template::Handlers::ERB
20
+ unless handler.ancestors.include?(::Floatable::Rails::ErbHandlerPatch)
21
+ handler.prepend(::Floatable::Rails::ErbHandlerPatch)
22
+ end
23
+
24
+ unless handler.erb_implementation == ::Floatable::Rails::ErubiWithFloatableViewMarkers
25
+ handler.erb_implementation = ::Floatable::Rails::ErubiWithFloatableViewMarkers
26
+ end
27
+ end
28
+
29
+ def apply_slim_patch!
30
+ return unless defined?(::Slim::RailsTemplate)
31
+
32
+ slim_handler = ::Slim::RailsTemplate
33
+ return if slim_handler.ancestors.include?(::Floatable::Rails::SlimHandlerPatch)
34
+
35
+ slim_handler.prepend(::Floatable::Rails::SlimHandlerPatch)
36
+ end
37
+ end
12
38
 
13
39
  initializer "floatable.rails.view_helpers" do
14
40
  ActiveSupport.on_load(:action_view) do
15
- include ::Floatable::Rails::Helpers
16
- prepend ::Floatable::Rails::TagInstrumentation
41
+ ::Floatable::Rails::Engine.with_fail_open_init("view_helpers") do
42
+ include ::Floatable::Rails::Helpers
43
+ end
17
44
  end
18
45
  end
19
46
 
20
47
  initializer "floatable.rails.erb_view_instrumentation" do
21
48
  ActiveSupport.on_load(:action_view) do
22
- ::ActionView::Template::Handlers::ERB.prepend(::Floatable::Rails::ErbHandlerPatch)
23
- ::ActionView::Template::Handlers::ERB.erb_implementation = ::Floatable::Rails::ErubiWithFloatableViewMarkers
49
+ ::Floatable::Rails::Engine.with_fail_open_init("erb_view_instrumentation") do
50
+ ::Floatable::Rails::Engine.apply_erb_patch!
51
+ end
52
+ end
53
+
54
+ config.after_initialize do
55
+ ::Floatable::Rails::Engine.with_fail_open_init("erb_view_instrumentation(after_initialize)") do
56
+ ::Floatable::Rails::Engine.apply_erb_patch!
57
+ end
24
58
  end
25
59
  end
26
60
 
27
61
  initializer "floatable.rails.slim_view_instrumentation" do
28
62
  ActiveSupport.on_load(:action_view) do
29
- next unless defined?(::Slim::RailsTemplate)
63
+ ::Floatable::Rails::Engine.with_fail_open_init("slim_view_instrumentation") do
64
+ ::Floatable::Rails::Engine.apply_slim_patch!
65
+ end
66
+ end
30
67
 
31
- ::Slim::RailsTemplate.prepend(::Floatable::Rails::SlimHandlerPatch)
68
+ config.after_initialize do
69
+ ::Floatable::Rails::Engine.with_fail_open_init("slim_view_instrumentation(after_initialize)") do
70
+ ::Floatable::Rails::Engine.apply_slim_patch!
71
+ end
32
72
  end
33
73
  end
34
74
 
35
75
  initializer "floatable.rails.controller_helpers" do
36
76
  ActiveSupport.on_load(:action_controller_base) do
37
- helper ::Floatable::Rails::Helpers
77
+ ::Floatable::Rails::Engine.with_fail_open_init("controller_helpers") do
78
+ helper ::Floatable::Rails::Helpers
79
+ end
38
80
  end
39
81
  end
40
82
 
41
83
  initializer "floatable.rails.middleware" do |app|
42
- app.middleware.use ::Floatable::Rails::ResponseMiddleware
84
+ ::Floatable::Rails::Engine.with_fail_open_init("middleware") do
85
+ app.middleware.use ::Floatable::Rails::ResponseMiddleware
86
+ end
43
87
  end
44
88
  end
45
89
  end
@@ -21,28 +21,37 @@ module Floatable
21
21
  }
22
22
 
23
23
  self.class.erb_implementation.new(erb, options).src
24
+ rescue => e
25
+ ::Floatable::Rails.log_error("[Floatable::Rails::ErbHandlerPatch] instrumentation failed: #{e.class} - #{e.message}")
26
+ super(template, source)
24
27
  end
25
28
  end
26
29
 
27
30
  class ErubiWithFloatableViewMarkers < ::ActionView::Template::Handlers::ERB::Erubi
28
31
  def initialize(input, properties = {})
29
- view_path = floatable_views_path(properties[:filename])
30
-
31
- if view_path.present?
32
- properties = properties.dup
33
- bufvar = properties[:bufvar] || "@output_buffer"
34
- postamble = properties[:postamble] || bufvar
35
-
36
- properties[:preamble] =
37
- "#{properties[:preamble]}" \
38
- "@output_buffer.safe_append='<!-- #{::Floatable::Rails::VIEW_COMMENT_BEGIN} #{view_path} -->' if Thread.current[:floatable_enabled];"
39
-
40
- properties[:postamble] =
41
- "@output_buffer.safe_append='<!-- #{::Floatable::Rails::VIEW_COMMENT_END} #{view_path} -->' if Thread.current[:floatable_enabled];" \
42
- "#{postamble}"
32
+ instrumented_properties = properties
33
+
34
+ begin
35
+ view_path = floatable_views_path(properties[:filename])
36
+
37
+ if view_path.present?
38
+ instrumented_properties = properties.dup
39
+ bufvar = instrumented_properties[:bufvar] || "@output_buffer"
40
+ postamble = instrumented_properties[:postamble] || bufvar
41
+
42
+ instrumented_properties[:preamble] =
43
+ "#{instrumented_properties[:preamble]}" \
44
+ "@output_buffer.safe_append='<!-- #{::Floatable::Rails::VIEW_COMMENT_BEGIN} #{view_path} -->' if Thread.current[:floatable_enabled];"
45
+
46
+ instrumented_properties[:postamble] =
47
+ "@output_buffer.safe_append='<!-- #{::Floatable::Rails::VIEW_COMMENT_END} #{view_path} -->' if Thread.current[:floatable_enabled];" \
48
+ "#{postamble}"
49
+ end
50
+ rescue => e
51
+ ::Floatable::Rails.log_error("[Floatable::Rails::ErubiWithFloatableViewMarkers] initialize instrumentation failed: #{e.class} - #{e.message}")
43
52
  end
44
53
 
45
- super
54
+ super(input, instrumented_properties)
46
55
  end
47
56
 
48
57
  private
@@ -5,6 +5,9 @@ module Floatable
5
5
  return false unless respond_to?(:request)
6
6
 
7
7
  ::Floatable::Rails.enabled_for?(request)
8
+ rescue => e
9
+ ::Floatable::Rails.log_error("[Floatable::Rails::Helpers] floatable_enabled? failed: #{e.class} - #{e.message}")
10
+ false
8
11
  end
9
12
 
10
13
  def floatable_tag
@@ -19,6 +22,9 @@ module Floatable
19
22
  defer: false,
20
23
  data: ::Floatable::Rails.script_data_attributes
21
24
  )
25
+ rescue => e
26
+ ::Floatable::Rails.log_error("[Floatable::Rails::Helpers] floatable_tag failed: #{e.class} - #{e.message}")
27
+ nil
22
28
  end
23
29
  end
24
30
  end
@@ -1,5 +1,4 @@
1
1
  require "nokogiri"
2
- require "base64"
3
2
 
4
3
  module Floatable
5
4
  module Rails
@@ -9,11 +8,13 @@ module Floatable
9
8
  end
10
9
 
11
10
  def call(env)
11
+ status = headers = body = nil
12
+ raw = nil
13
+
12
14
  enabled = floatable_enabled?(env)
13
15
  instrument = enabled && ::Floatable::Rails.instrumentation_enabled?
14
16
  Thread.current[:floatable_enabled] = instrument
15
17
 
16
- status = headers = body = nil
17
18
  begin
18
19
  status, headers, body = @app.call(env)
19
20
  ensure
@@ -23,9 +24,7 @@ module Floatable
23
24
  return [ status, headers, body ] unless enabled
24
25
  return [ status, headers, body ] unless html_response?(status, headers)
25
26
 
26
- raw = +""
27
- body.each { |chunk| raw << chunk.to_s }
28
- body.close if body.respond_to?(:close)
27
+ raw = read_body(body)
29
28
 
30
29
  instrumented = instrument ? instrument_html(raw) : raw
31
30
  final_html = inject_script(instrumented)
@@ -34,23 +33,29 @@ module Floatable
34
33
 
35
34
  [ status, headers, [ final_html ] ]
36
35
  rescue => e
37
- ::Rails.logger.error("[Floatable::Rails::ResponseMiddleware] failed: #{e.class} - #{e.message}")
38
- if e.backtrace
39
- e.backtrace.first(30).each do |line|
40
- ::Rails.logger.error(" #{line}")
41
- end
42
- end
43
-
44
- raise if ::Rails.env.development? || ::Rails.env.test?
36
+ ::Floatable::Rails.log_error("[Floatable::Rails::ResponseMiddleware] failed: #{e.class} - #{e.message}")
37
+ ::Floatable::Rails.log_backtrace(e)
38
+ return [ status, headers, [ raw ] ] if raw
39
+ return [ status, headers, body ] if status && headers && body
45
40
 
46
- status ||= 500
47
- headers ||= {}
48
- body ||= []
49
- [ status, headers, body ]
41
+ raise
50
42
  end
51
43
 
52
44
  private
53
45
 
46
+ def read_body(body)
47
+ raw = +""
48
+ return raw if body.nil?
49
+
50
+ body.each { |chunk| raw << chunk.to_s }
51
+ raw
52
+ rescue => e
53
+ ::Floatable::Rails.log_error("[Floatable::Rails::ResponseMiddleware] read_body failed: #{e.class} - #{e.message}")
54
+ raw
55
+ ensure
56
+ body.close if body.respond_to?(:close)
57
+ end
58
+
54
59
  def html_response?(status, headers)
55
60
  return false unless status == 200
56
61
  ct = headers["Content-Type"] || headers["content-type"]
@@ -61,7 +66,7 @@ module Floatable
61
66
  req = ::Rack::Request.new(env)
62
67
  ::Floatable::Rails.enabled_for?(req)
63
68
  rescue => e
64
- ::Rails.logger.error("[Floatable::Rails::ResponseMiddleware] enabled? failed: #{e.class} - #{e.message}")
69
+ ::Floatable::Rails.log_error("[Floatable::Rails::ResponseMiddleware] enabled? failed: #{e.class} - #{e.message}")
65
70
  false
66
71
  end
67
72
 
@@ -70,26 +75,19 @@ module Floatable
70
75
 
71
76
  doctype = html[/\A\s*<!DOCTYPE[^>]*>/i]
72
77
  doc = parse_html(html)
73
-
74
- doc.css("*").each do |node|
75
- name = node.name.to_s.downcase
76
- next if %w[script style].include?(name)
77
- end
78
-
79
- apply_view_markers_and_assign_ids(doc)
78
+ apply_view_markers_and_assign_paths(doc)
80
79
 
81
80
  rendered = doc.to_html
82
81
  return rendered if doctype.nil? || rendered.lstrip.start_with?("<!DOCTYPE")
83
82
 
84
83
  "#{doctype}\n#{rendered}"
85
84
  rescue => e
86
- ::Rails.logger.debug("[Floatable::Rails] HTML instrumentation failed: #{e.class} - #{e.message}")
85
+ ::Floatable::Rails.log_debug("[Floatable::Rails] HTML instrumentation failed: #{e.class} - #{e.message}")
87
86
  html
88
87
  end
89
88
 
90
- def apply_view_markers_and_assign_ids(doc)
89
+ def apply_view_markers_and_assign_paths(doc)
91
90
  view_stack = []
92
- counters = Hash.new(0)
93
91
 
94
92
  doc.traverse do |node|
95
93
  if node.comment?
@@ -119,17 +117,12 @@ module Floatable
119
117
  next if current_path.blank?
120
118
 
121
119
  node["data-floatable-path"] ||= current_path
122
- if node["data-floatable-id"].blank?
123
- counters[current_path] += 1
124
- encoded = Base64.urlsafe_encode64(counters[current_path].to_s, padding: false)
125
- node["data-floatable-id"] = "#{current_path}:#{encoded}"
126
- end
127
120
  end
128
121
 
129
122
  # Remove all comments, including our markers.
130
123
  doc.xpath("//comment()").remove
131
124
  rescue => e
132
- ::Rails.logger.debug("[Floatable::Rails] apply_view_markers_and_assign_ids failed: #{e.class} - #{e.message}")
125
+ ::Floatable::Rails.log_debug("[Floatable::Rails] apply_view_markers_and_assign_paths failed: #{e.class} - #{e.message}")
133
126
  end
134
127
 
135
128
  def parse_html(html)
@@ -162,7 +155,7 @@ module Floatable
162
155
  html + snippet
163
156
  end
164
157
  rescue => e
165
- ::Rails.logger.error("[Floatable::Rails::ResponseMiddleware] inject_script failed: #{e.class} - #{e.message}")
158
+ ::Floatable::Rails.log_error("[Floatable::Rails::ResponseMiddleware] inject_script failed: #{e.class} - #{e.message}")
166
159
  html
167
160
  end
168
161
  end
@@ -54,7 +54,14 @@ module Floatable
54
54
  view_path = floatable_views_path(template.identifier)
55
55
  return super if view_path.nil?
56
56
 
57
- instrumented = SlimViewMarkerInstrumenter.instrument(source || template.source, view_path)
57
+ instrumented =
58
+ begin
59
+ SlimViewMarkerInstrumenter.instrument(source || template.source, view_path)
60
+ rescue => e
61
+ ::Floatable::Rails.log_error("[Floatable::Rails::SlimHandlerPatch] instrumentation failed: #{e.class} - #{e.message}")
62
+ return super
63
+ end
64
+
58
65
  super(template, instrumented)
59
66
  end
60
67
 
@@ -1,5 +1,5 @@
1
1
  module Floatable
2
2
  module Rails
3
- VERSION = "0.1.5"
3
+ VERSION = "0.1.6"
4
4
  end
5
5
  end
@@ -46,6 +46,28 @@ module Floatable
46
46
  end
47
47
 
48
48
  class << self
49
+ def log_error(message)
50
+ ::Rails.logger&.error(message)
51
+ rescue
52
+ nil
53
+ end
54
+
55
+ def log_debug(message)
56
+ ::Rails.logger&.debug(message)
57
+ rescue
58
+ nil
59
+ end
60
+
61
+ def log_backtrace(error, limit: 30)
62
+ return unless error&.backtrace
63
+
64
+ error.backtrace.first(limit).each do |line|
65
+ log_error(" #{line}")
66
+ end
67
+ rescue
68
+ nil
69
+ end
70
+
49
71
  def config
50
72
  @config ||= Configuration.new
51
73
  end
@@ -61,16 +83,22 @@ module Floatable
61
83
 
62
84
  !!predicate.call(request)
63
85
  rescue => e
64
- ::Rails.logger.error("[Floatable::Rails] enabled_for? failed: #{e.class} - #{e.message}")
86
+ log_error("[Floatable::Rails] enabled_for? failed: #{e.class} - #{e.message}")
65
87
  false
66
88
  end
67
89
 
68
90
  def instrumentation_enabled?
69
91
  config.instrumentation_enabled != false
92
+ rescue => e
93
+ log_error("[Floatable::Rails] instrumentation_enabled? failed: #{e.class} - #{e.message}")
94
+ false
70
95
  end
71
96
 
72
97
  def script_src
73
98
  config.script_src
99
+ rescue => e
100
+ log_error("[Floatable::Rails] script_src failed: #{e.class} - #{e.message}")
101
+ nil
74
102
  end
75
103
 
76
104
  def script_data
@@ -81,10 +109,16 @@ module Floatable
81
109
  branch: config.script_branch,
82
110
  agent_tools_mode: config.script_agent_tools_mode
83
111
  }.reject { |_key, value| value.nil? || (value.respond_to?(:empty?) && value.empty?) }
112
+ rescue => e
113
+ log_error("[Floatable::Rails] script_data failed: #{e.class} - #{e.message}")
114
+ {}
84
115
  end
85
116
 
86
117
  def script_data_attributes
87
118
  script_data.transform_keys { |key| :"floatable_#{key}" }
119
+ rescue => e
120
+ log_error("[Floatable::Rails] script_data_attributes failed: #{e.class} - #{e.message}")
121
+ {}
88
122
  end
89
123
 
90
124
  def script_data_attributes_html
@@ -94,6 +128,9 @@ module Floatable
94
128
  data.map do |key, value|
95
129
  %( data-floatable-#{key.to_s.tr("_", "-")}="#{ERB::Util.html_escape(value.to_s)}")
96
130
  end.join
131
+ rescue => e
132
+ log_error("[Floatable::Rails] script_data_attributes_html failed: #{e.class} - #{e.message}")
133
+ ""
97
134
  end
98
135
  end
99
136
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: floatable-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Almstrand
@@ -41,7 +41,6 @@ files:
41
41
  - lib/floatable/rails/helpers.rb
42
42
  - lib/floatable/rails/response_middleware.rb
43
43
  - lib/floatable/rails/slim_view_instrumentation.rb
44
- - lib/floatable/rails/tag_instrumentation.rb
45
44
  - lib/floatable/rails/version.rb
46
45
  homepage: https://floatable.dev
47
46
  licenses:
@@ -1,181 +0,0 @@
1
- require "pathname"
2
- require "base64"
3
-
4
- module Floatable
5
- module Rails
6
- module TagInstrumentation
7
- def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
8
- if floatable_enabled_for_view?
9
- options = ensure_options_hash(content_or_options_with_block, options)
10
- file, line = floatable_callsite
11
-
12
- if file
13
- options[:"data-floatable-path"] ||= floatable_relative_path(file)
14
- if line
15
- encoded = Base64.urlsafe_encode64(line.to_s, padding: false)
16
- options[:"data-floatable-id"] ||= "#{options[:"data-floatable-path"]}:#{encoded}"
17
- end
18
- end
19
-
20
- # Re-shape args for super if we mutated them
21
- if content_or_options_with_block.is_a?(Hash) && options
22
- content_or_options_with_block = options
23
- options = nil
24
- end
25
- end
26
-
27
- super
28
- end
29
-
30
- def tag(name = nil, options = nil, open = false, escape = true)
31
- if floatable_enabled_for_view? && name
32
- options = (options || {}).dup
33
- file, line = floatable_callsite
34
-
35
- if file
36
- options[:"data-floatable-path"] ||= floatable_relative_path(file)
37
- if line
38
- encoded = Base64.urlsafe_encode64(line.to_s, padding: false)
39
- options[:"data-floatable-id"] ||= "#{options[:"data-floatable-path"]}:#{encoded}"
40
- end
41
- end
42
-
43
- return super(name, options, open, escape)
44
- end
45
-
46
- super
47
- end
48
-
49
- private
50
-
51
- def floatable_enabled_for_view?
52
- return false unless respond_to?(:request)
53
-
54
- ::Floatable::Rails.instrumentation_enabled? && ::Floatable::Rails.enabled_for?(request)
55
- rescue => e
56
- ::Rails.logger.error("[Floatable::Rails::TagInstrumentation] enabled? failed: #{e.class} - #{e.message}")
57
- false
58
- end
59
-
60
- def ensure_options_hash(content_or_options_with_block, options)
61
- if content_or_options_with_block.is_a?(Hash) && options.nil?
62
- content_or_options_with_block
63
- else
64
- options || {}
65
- end
66
- end
67
-
68
- # Callsite detection
69
- #
70
- # We want the *template* or app file (e.g. app/views/posts/index.html.erb),
71
- # not ActionView's gem helpers.
72
- #
73
- # Strategy:
74
- # - Walk the call stack.
75
- # - Pick the first frame whose path starts with Rails.root
76
- # (so we ignore anything under ~/.rbenv, /gems/, etc).
77
- # - Prefer files under app/views, but accept any app file as fallback.
78
- #
79
- def floatable_callsite
80
- app_root = ::Rails.root.to_s
81
- return [ nil, nil ] if app_root.blank?
82
-
83
- locations = caller_locations(2, 200) || []
84
-
85
- template_path = floatable_current_template_path
86
- if template_path
87
- loc = locations.find { |loc_item| loc_item.path.to_s == template_path }
88
- return [ template_path, loc.lineno ] if loc
89
- end
90
-
91
- # First pass: prefer app/views templates, excluding layouts when possible.
92
- view_locs = locations.select do |loc|
93
- path = loc.path.to_s
94
- path.start_with?(app_root) && path.include?("/app/views/") && path.end_with?(".erb")
95
- end
96
-
97
- unless view_locs.empty?
98
- non_layout = view_locs.reverse.find { |loc| !loc.path.to_s.include?("/app/views/layouts/") }
99
- chosen = non_layout || view_locs.last
100
- return [ chosen.path, chosen.lineno ]
101
- end
102
-
103
- # Second pass: any file under Rails.root (app/models, app/controllers, etc)
104
- app_loc = locations.find do |loc|
105
- path = loc.path.to_s
106
- path.start_with?(app_root)
107
- end
108
-
109
- if app_loc
110
- return [ app_loc.path, app_loc.lineno ]
111
- end
112
-
113
- # If nothing inside the app, we give up and return nil
114
- [ nil, nil ]
115
- rescue => e
116
- ::Rails.logger.debug("[Floatable::Rails::TagInstrumentation] floatable_callsite failed: #{e.class} - #{e.message}")
117
- [ nil, nil ]
118
- end
119
-
120
- def floatable_current_template_path
121
- return nil unless respond_to?(:lookup_context)
122
-
123
- current_template = floatable_current_template_identifier
124
- return current_template if current_template.present?
125
-
126
- vpath = floatable_virtual_path
127
- return nil if vpath.empty?
128
-
129
- templates = lookup_context.find_all(vpath, [], true)
130
- template = templates.first || lookup_context.find_all(vpath, [], false).first
131
- identifier = template&.identifier.to_s
132
- identifier.presence
133
- rescue
134
- nil
135
- end
136
-
137
- def floatable_current_template_identifier
138
- return nil unless instance_variable_defined?(:@current_template)
139
-
140
- template = instance_variable_get(:@current_template)
141
- identifier = template&.identifier.to_s
142
- identifier.presence
143
- rescue
144
- nil
145
- end
146
-
147
- def floatable_virtual_path
148
- vpath = nil
149
- vpath = virtual_path.to_s if respond_to?(:virtual_path)
150
-
151
- if vpath.to_s.empty?
152
- vpath = instance_variable_get(:@virtual_path).to_s if instance_variable_defined?(:@virtual_path)
153
- end
154
-
155
- if vpath.to_s.empty?
156
- vpath = instance_variable_get(:@_virtual_path).to_s if instance_variable_defined?(:@_virtual_path)
157
- end
158
-
159
- vpath.to_s
160
- rescue
161
- ""
162
- end
163
-
164
- def floatable_relative_path(file)
165
- return nil if file.blank?
166
-
167
- app_root = ::Rails.root.to_s
168
- return file unless app_root.present?
169
-
170
- # Only record paths inside the app root; ignore gems/rbenv/etc.
171
- unless file.start_with?(app_root)
172
- return nil
173
- end
174
-
175
- Pathname.new(file).relative_path_from(Pathname.new(app_root)).to_s
176
- rescue
177
- file
178
- end
179
- end
180
- end
181
- end