bullet 7.2.0 → 8.0.8

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.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Bullet::Ext::Object
4
+
3
5
  module Bullet
4
6
  module Detector
5
7
  class Association < Base
@@ -34,7 +36,7 @@ module Bullet
34
36
  # that the objects may cause N+1 query.
35
37
  # e.g. { Post => ["Post:1", "Post:2"] }
36
38
  def possible_objects
37
- Thread.current[:bullet_possible_objects]
39
+ Thread.current.thread_variable_get(:bullet_possible_objects)
38
40
  end
39
41
 
40
42
  # impossible_objects keep the class to objects relationships
@@ -43,7 +45,7 @@ module Bullet
43
45
  # if find collection returns only one object, then the object is impossible object,
44
46
  # impossible_objects are used to avoid treating 1+1 query to N+1 query.
45
47
  def impossible_objects
46
- Thread.current[:bullet_impossible_objects]
48
+ Thread.current.thread_variable_get(:bullet_impossible_objects)
47
49
  end
48
50
 
49
51
  private
@@ -54,7 +56,7 @@ module Bullet
54
56
  # the object_associations keep all associations that may be or may no be
55
57
  # unpreload associations or unused preload associations.
56
58
  def object_associations
57
- Thread.current[:bullet_object_associations]
59
+ Thread.current.thread_variable_get(:bullet_object_associations)
58
60
  end
59
61
 
60
62
  # call_object_associations keep the object relationships
@@ -62,27 +64,27 @@ module Bullet
62
64
  # e.g. { "Post:1" => [:comments] }
63
65
  # they are used to detect unused preload associations.
64
66
  def call_object_associations
65
- Thread.current[:bullet_call_object_associations]
67
+ Thread.current.thread_variable_get(:bullet_call_object_associations)
66
68
  end
67
69
 
68
70
  # inversed_objects keeps object relationships
69
71
  # that association is inversed.
70
72
  # e.g. { "Comment:1" => ["post"] }
71
73
  def inversed_objects
72
- Thread.current[:bullet_inversed_objects]
74
+ Thread.current.thread_variable_get(:bullet_inversed_objects)
73
75
  end
74
76
 
75
77
  # eager_loadings keep the object relationships
76
78
  # that the associations are preloaded by find :include.
77
79
  # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
78
80
  def eager_loadings
79
- Thread.current[:bullet_eager_loadings]
81
+ Thread.current.thread_variable_get(:bullet_eager_loadings)
80
82
  end
81
83
 
82
- # cal_stacks keeps stacktraces where querie-objects were called from.
84
+ # call_stacks keeps stacktraces where querie-objects were called from.
83
85
  # e.g. { 'Object:111' => [SomeProject/app/controllers/...] }
84
86
  def call_stacks
85
- Thread.current[:bullet_call_stacks]
87
+ Thread.current.thread_variable_get(:bullet_call_stacks)
86
88
  end
87
89
  end
88
90
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Bullet::Ext::Object
4
+
3
5
  module Bullet
4
6
  module Detector
5
7
  class CounterCache < Base
@@ -44,11 +46,11 @@ module Bullet
44
46
  end
45
47
 
46
48
  def possible_objects
47
- Thread.current[:bullet_counter_possible_objects]
49
+ Thread.current.thread_variable_get(:bullet_counter_possible_objects)
48
50
  end
49
51
 
50
52
  def impossible_objects
51
- Thread.current[:bullet_counter_impossible_objects]
53
+ Thread.current.thread_variable_get(:bullet_counter_impossible_objects)
52
54
  end
53
55
 
54
56
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Bullet::Ext::Object
4
+
3
5
  module Bullet
4
6
  module Detector
5
7
  class NPlusOneQuery < Association
@@ -25,7 +27,7 @@ module Bullet
25
27
  )
26
28
  if !excluded_stacktrace_path? && conditions_met?(object, associations)
27
29
  Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
28
- create_notification caller_in_project(object.bullet_key), object.class.to_s, associations
30
+ create_notification(caller_in_project(object.bullet_key), object.class.to_s, associations)
29
31
  end
30
32
  end
31
33
 
@@ -36,16 +38,17 @@ module Bullet
36
38
  objects = Array.wrap(object_or_objects)
37
39
  class_names_match_regex = true
38
40
  primary_key_values_are_empty = true
39
- keys_joined = ""
40
- objects.each do |obj|
41
+
42
+ keys_joined = objects.map do |obj|
41
43
  unless obj.class.name =~ /^HABTM_/
42
44
  class_names_match_regex = false
43
45
  end
44
46
  unless obj.bullet_primary_key_value.nil?
45
47
  primary_key_values_are_empty = false
46
48
  end
47
- keys_joined += "#{(keys_joined.empty? ? '' : ', ')}#{obj.bullet_key}"
48
- end
49
+ obj.bullet_key
50
+ end.join(", ")
51
+
49
52
  unless class_names_match_regex || primary_key_values_are_empty
50
53
  Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{keys_joined}")
51
54
  objects.each { |object| possible_objects.add object.bullet_key }
@@ -64,13 +67,23 @@ module Bullet
64
67
  def add_inversed_object(object, association)
65
68
  return unless Bullet.start?
66
69
  return unless Bullet.n_plus_one_query_enable?
67
- return unless object.bullet_primary_key_value
68
70
 
71
+ object_key = object.bullet_primary_key_value ? object.bullet_key : object.object_id
69
72
  Bullet.debug(
70
73
  'Detector::NPlusOneQuery#add_inversed_object',
71
- "object: #{object.bullet_key}, association: #{association}"
74
+ "object: #{object_key}, association: #{association}"
72
75
  )
73
- inversed_objects.add object.bullet_key, association
76
+ inversed_objects.add object_key, association
77
+ end
78
+
79
+ def update_inversed_object(object)
80
+ if inversed_objects&.key?(object.object_id)
81
+ Bullet.debug(
82
+ 'Detector::NPlusOneQuery#update_inversed_object',
83
+ "object from #{object.object_id} to #{object.bullet_key}"
84
+ )
85
+ inversed_objects.add(object.bullet_key, inversed_objects[object.object_id].to_a)
86
+ end
74
87
  end
75
88
 
76
89
  # decide whether the object.associations is unpreloaded or not.
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Bullet::Ext::Object
4
+ using Bullet::Ext::String
5
+
3
6
  module Bullet
4
7
  module Detector
5
8
  class UnusedEagerLoading < Association
@@ -1,30 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Object
4
- def bullet_key
5
- "#{self.class}:#{bullet_primary_key_value}"
6
- end
3
+ module Bullet
4
+ module Ext
5
+ module Object
6
+ refine ::Object do
7
+ attr_writer :bullet_key, :bullet_primary_key_value
7
8
 
8
- def bullet_primary_key_value
9
- return if respond_to?(:persisted?) && !persisted?
9
+ def bullet_key
10
+ return "#{self.class}:" if respond_to?(:persisted?) && !persisted?
10
11
 
11
- if self.class.respond_to?(:primary_keys) && self.class.primary_keys
12
- primary_key = self.class.primary_keys
13
- elsif self.class.respond_to?(:primary_key) && self.class.primary_key
14
- primary_key = self.class.primary_key
15
- else
16
- primary_key = :id
17
- end
12
+ @bullet_key ||= "#{self.class}:#{bullet_primary_key_value}"
13
+ end
18
14
 
19
- bullet_join_potential_composite_primary_key(primary_key)
20
- end
15
+ def bullet_primary_key_value
16
+ return if respond_to?(:persisted?) && !persisted?
21
17
 
22
- private
18
+ @bullet_primary_key_value ||=
19
+ begin
20
+ primary_key = self.class.try(:primary_keys) || self.class.try(:primary_key) || :id
23
21
 
24
- def bullet_join_potential_composite_primary_key(primary_keys)
25
- return send(primary_keys) unless primary_keys.is_a?(Enumerable)
22
+ bullet_join_potential_composite_primary_key(primary_key)
23
+ end
24
+ end
26
25
 
27
- primary_keys.map { |primary_key| send primary_key }
28
- .join(',')
26
+ private
27
+
28
+ def bullet_join_potential_composite_primary_key(primary_keys)
29
+ return read_attribute(primary_keys) unless primary_keys.is_a?(Enumerable)
30
+
31
+ primary_keys.map { |primary_key| read_attribute primary_key }
32
+ .compact.join(',')
33
+ end
34
+ end
35
+ end
29
36
  end
30
37
  end
@@ -1,7 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class String
4
- def bullet_class_name
5
- sub(/:[^:]*?$/, '')
3
+ module Bullet
4
+ module Ext
5
+ module String
6
+ refine ::String do
7
+ def bullet_class_name
8
+ last_colon = self.rindex(':')
9
+ last_colon ? self[0...last_colon].dup : self.dup
10
+ end
11
+ end
12
+ end
6
13
  end
7
14
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ require 'rubygems'
8
+ ::Mongoid::Contextual::Mongo.class_eval do
9
+ alias_method :origin_first, :first
10
+ alias_method :origin_last, :last
11
+ alias_method :origin_each, :each
12
+ alias_method :origin_eager_load, :eager_load
13
+
14
+ %i[first last].each do |context|
15
+ default = Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('7.5') ? nil : {}
16
+ define_method(context) do |opts = default|
17
+ result = send(:"origin_#{context}", opts)
18
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
19
+ result
20
+ end
21
+ end
22
+
23
+ def each(&_block)
24
+ return to_enum unless block_given?
25
+
26
+ first_document = nil
27
+ document_count = 0
28
+
29
+ origin_each do |document|
30
+ document_count += 1
31
+
32
+ if document_count == 1
33
+ first_document = document
34
+ elsif document_count == 2
35
+ Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
36
+ yield(first_document)
37
+ first_document = nil
38
+ yield(document)
39
+ else
40
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
41
+ yield(document)
42
+ end
43
+ end
44
+
45
+ if document_count == 1
46
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
47
+ yield(first_document)
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ def eager_load(docs)
54
+ associations = criteria.inclusions.map(&:name)
55
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
56
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
57
+ origin_eager_load(docs)
58
+ end
59
+ end
60
+
61
+ ::Mongoid::Association::Accessors.class_eval do
62
+ alias_method :origin_get_relation, :get_relation
63
+
64
+ def get_relation(name, association, object, reload = false)
65
+ result = origin_get_relation(name, association, object, reload)
66
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) unless association.embedded?
67
+ result
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/bullet/rack.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rack/request'
4
+ require 'json'
5
+ require 'cgi'
6
+
3
7
  module Bullet
4
8
  class Rack
5
9
  include Dependency
6
10
 
7
- NONCE_MATCHER = /script-src .*'nonce-(?<nonce>[A-Za-z0-9+\/]+={0,2})'/
11
+ NONCE_MATCHER = /(script|style)-src .*'nonce-(?<nonce>[A-Za-z0-9+\/]+={0,2})'/
8
12
 
9
13
  def initialize(app)
10
14
  @app = app
@@ -19,12 +23,13 @@ module Bullet
19
23
  response_body = nil
20
24
 
21
25
  if Bullet.notification? || Bullet.always_append_html_body
22
- if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
26
+ request = ::Rack::Request.new(env)
27
+ if Bullet.inject_into_page? && !skip_html_injection?(request) && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
23
28
  if html_request?(headers, response)
24
29
  response_body = response_body(response)
25
30
 
26
31
  with_security_policy_nonce(headers) do |nonce|
27
- response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
32
+ response_body = append_to_html_body(response_body, footer_note(nonce)) if Bullet.add_footer
28
33
  response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
29
34
  if Bullet.add_footer && !Bullet.skip_http_headers
30
35
  response_body = append_to_html_body(response_body, xhr_script(nonce))
@@ -65,16 +70,48 @@ module Bullet
65
70
  end
66
71
  end
67
72
 
68
- def footer_note
69
- "<details #{details_attributes}><summary #{summary_attributes}>Bullet Warnings</summary><div #{footer_content_attributes}>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message}</div></details>"
73
+ def footer_note(nonce = nil)
74
+ %(<details id="bullet-footer" data-is-bullet-footer><summary>Bullet Warnings</summary><div>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message(nonce)}</div>#{footer_style(nonce)}</details>)
75
+ end
76
+
77
+ # Make footer styles work with ContentSecurityPolicy style-src as self
78
+ def footer_style(nonce = nil)
79
+ css = <<~CSS
80
+ details#bullet-footer {cursor: pointer; position: fixed; left: 0px; bottom: 0px; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; border-radius: 0px 8px 0px 0px; border: 1px solid #9b1c1c;}
81
+ details#bullet-footer summary {font-weight: 600; padding: 2px 8px;}
82
+ details#bullet-footer div {padding: 8px; border-top: 1px solid #9b1c1c;}
83
+ CSS
84
+ if nonce
85
+ %(<style type="text/css" nonce="#{nonce}">#{css}</style>)
86
+ else
87
+ %(<style type="text/css">#{css}</style>)
88
+ end
70
89
  end
71
90
 
72
91
  def set_header(headers, header_name, header_array)
73
92
  # Many proxy applications such as Nginx and AWS ELB limit
74
93
  # the size a header to 8KB, so truncate the list of reports to
75
94
  # be under that limit
76
- header_array.pop while header_array.to_json.length > 8 * 1024
77
- headers[header_name] = header_array.to_json
95
+ header_array.pop while JSON.generate(header_array).length > 8 * 1024
96
+ headers[header_name] = JSON.generate(header_array)
97
+ end
98
+
99
+ def skip_html_injection?(request)
100
+ query_string = request.env['QUERY_STRING']
101
+ return false if query_string.nil? || query_string.empty?
102
+
103
+ params = simple_parse_query_string(query_string)
104
+ params['skip_html_injection'] == 'true'
105
+ end
106
+
107
+ # Simple query string parser
108
+ def simple_parse_query_string(query_string)
109
+ params = {}
110
+ query_string.split('&').each do |pair|
111
+ key, value = pair.split('=', 2).map { |s| CGI.unescape(s) }
112
+ params[key] = value if key && !key.empty?
113
+ end
114
+ params
78
115
  end
79
116
 
80
117
  def file?(headers)
@@ -99,28 +136,18 @@ module Bullet
99
136
 
100
137
  private
101
138
 
102
- def details_attributes
103
- <<~EOF
104
- id="bullet-footer" data-is-bullet-footer
105
- style="cursor: pointer; position: fixed; left: 0px; bottom: 0px; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; border-radius: 0px 8px 0px 0px; border: 1px solid #9b1c1c;"
106
- EOF
107
- end
108
-
109
- def summary_attributes
110
- <<~EOF
111
- style="font-weight: 600; padding: 2px 8px"
112
- EOF
113
- end
114
-
115
- def footer_content_attributes
116
- <<~EOF
117
- style="padding: 8px; border-top: 1px solid #9b1c1c;"
118
- EOF
119
- end
120
-
121
- def footer_console_message
139
+ def footer_console_message(nonce = nil)
122
140
  if Bullet.console_enabled?
123
- "<br/><span style='font-style: italic;'>See 'Uniform Notifier' in JS Console for Stacktrace</span>"
141
+ footer = %(<br/><span id="console-message">See 'Uniform Notifier' in JS Console for Stacktrace</span>)
142
+ css = "details#bullet-footer #console-message {font-style: italic;}"
143
+ style =
144
+ if nonce
145
+ %(<style type="text/css" nonce="#{nonce}">#{css}</style>)
146
+ else
147
+ %(<style type="text/css">#{css}</style>)
148
+ end
149
+
150
+ footer + style
124
151
  end
125
152
  end
126
153
 
@@ -35,7 +35,11 @@ module Bullet
35
35
  end
36
36
 
37
37
  def include?(key, value)
38
- !@registry[key].nil? && @registry[key].include?(value)
38
+ key?(key) && @registry[key].include?(value)
39
+ end
40
+
41
+ def key?(key)
42
+ @registry.key?(key)
39
43
  end
40
44
  end
41
45
  end
@@ -5,7 +5,7 @@ module Bullet
5
5
  class CallStack < Base
6
6
  # remembers found association backtrace
7
7
  def add(key)
8
- @registry[key] = Thread.current.backtrace
8
+ @registry[key] ||= Thread.current.backtrace
9
9
  end
10
10
  end
11
11
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Bullet::Ext::Object
4
+ using Bullet::Ext::String
5
+
3
6
  module Bullet
4
7
  module Registry
5
8
  class Object < Base
@@ -2,10 +2,11 @@
2
2
 
3
3
  require "bundler"
4
4
 
5
+ using Bullet::Ext::Object
6
+
5
7
  module Bullet
6
8
  module StackTraceFilter
7
9
  VENDOR_PATH = '/vendor'
8
- IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
9
10
 
10
11
  # @param bullet_key[String] - use this to get stored call stack from call_stacks object.
11
12
  def caller_in_project(bullet_key = nil)
@@ -54,13 +55,12 @@ module Bullet
54
55
  def location_as_path(location)
55
56
  return location if location.is_a?(String)
56
57
 
57
- IS_RUBY_19 ? location : location.absolute_path.to_s
58
+ location.absolute_path.to_s
58
59
  end
59
60
 
60
61
  def select_caller_locations(bullet_key = nil)
61
- return caller.select { |caller_path| yield caller_path } if IS_RUBY_19
62
-
63
62
  call_stack = bullet_key ? call_stacks[bullet_key] : caller_locations
63
+
64
64
  call_stack.select { |location| yield location }
65
65
  end
66
66
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '7.2.0'
4
+ VERSION = '8.0.8'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/string/inflections'
3
4
  require 'active_support/core_ext/module/delegation'
4
5
  require 'set'
5
6
  require 'uniform_notifier'
@@ -22,8 +23,8 @@ module Bullet
22
23
 
23
24
  if defined?(Rails::Railtie)
24
25
  class BulletRailtie < Rails::Railtie
25
- initializer 'bullet.configure_rails_initialization' do |app|
26
- if defined?(ActionDispatch::ContentSecurityPolicy::Middleware) && Rails.application.config.content_security_policy
26
+ initializer 'bullet.add_middleware', after: :load_config_initializers do |app|
27
+ if defined?(ActionDispatch::ContentSecurityPolicy::Middleware) && Rails.application.config.content_security_policy && !app.config.api_only
27
28
  app.middleware.insert_before ActionDispatch::ContentSecurityPolicy::Middleware, Bullet::Rack
28
29
  else
29
30
  app.middleware.use Bullet::Rack
@@ -63,7 +64,7 @@ module Bullet
63
64
  ].freeze
64
65
 
65
66
  def enable=(enable)
66
- @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
67
+ @enable = enable
67
68
 
68
69
  if enable?
69
70
  reset_safelist
@@ -89,15 +90,15 @@ module Bullet
89
90
  end
90
91
 
91
92
  def n_plus_one_query_enable?
92
- enable? && !!@n_plus_one_query_enable
93
+ enable? && (@n_plus_one_query_enable.nil? ? true : @n_plus_one_query_enable)
93
94
  end
94
95
 
95
96
  def unused_eager_loading_enable?
96
- enable? && !!@unused_eager_loading_enable
97
+ enable? && (@unused_eager_loading_enable.nil? ? true : @unused_eager_loading_enable)
97
98
  end
98
99
 
99
100
  def counter_cache_enable?
100
- enable? && !!@counter_cache_enable
101
+ enable? && (@counter_cache_enable.nil? ? true : @counter_cache_enable)
101
102
  end
102
103
 
103
104
  def stacktrace_includes
@@ -148,42 +149,47 @@ module Bullet
148
149
  end
149
150
 
150
151
  def start_request
151
- Thread.current[:bullet_start] = true
152
- Thread.current[:bullet_notification_collector] = Bullet::NotificationCollector.new
153
-
154
- Thread.current[:bullet_object_associations] = Bullet::Registry::Base.new
155
- Thread.current[:bullet_call_object_associations] = Bullet::Registry::Base.new
156
- Thread.current[:bullet_possible_objects] = Bullet::Registry::Object.new
157
- Thread.current[:bullet_impossible_objects] = Bullet::Registry::Object.new
158
- Thread.current[:bullet_inversed_objects] = Bullet::Registry::Base.new
159
- Thread.current[:bullet_eager_loadings] = Bullet::Registry::Association.new
160
- Thread.current[:bullet_call_stacks] = Bullet::Registry::CallStack.new
152
+ Thread.current.thread_variable_set(:bullet_start, true)
153
+ Thread.current.thread_variable_set(:bullet_notification_collector, Bullet::NotificationCollector.new)
154
+
155
+ Thread.current.thread_variable_set(:bullet_object_associations, Bullet::Registry::Base.new)
156
+ Thread.current.thread_variable_set(:bullet_call_object_associations, Bullet::Registry::Base.new)
157
+ Thread.current.thread_variable_set(:bullet_possible_objects, Bullet::Registry::Object.new)
158
+ Thread.current.thread_variable_set(:bullet_impossible_objects, Bullet::Registry::Object.new)
159
+ Thread.current.thread_variable_set(:bullet_inversed_objects, Bullet::Registry::Base.new)
160
+ Thread.current.thread_variable_set(:bullet_eager_loadings, Bullet::Registry::Association.new)
161
+ Thread.current.thread_variable_set(:bullet_call_stacks, Bullet::Registry::CallStack.new)
162
+
163
+ unless Thread.current.thread_variable_get(:bullet_counter_possible_objects)
164
+ Thread.current.thread_variable_set(:bullet_counter_possible_objects, Bullet::Registry::Object.new)
165
+ end
161
166
 
162
- Thread.current[:bullet_counter_possible_objects] ||= Bullet::Registry::Object.new
163
- Thread.current[:bullet_counter_impossible_objects] ||= Bullet::Registry::Object.new
167
+ unless Thread.current.thread_variable_get(:bullet_counter_impossible_objects)
168
+ Thread.current.thread_variable_set(:bullet_counter_impossible_objects, Bullet::Registry::Object.new)
169
+ end
164
170
  end
165
171
 
166
172
  def end_request
167
- Thread.current[:bullet_start] = nil
168
- Thread.current[:bullet_notification_collector] = nil
173
+ Thread.current.thread_variable_set(:bullet_start, nil)
174
+ Thread.current.thread_variable_set(:bullet_notification_collector, nil)
169
175
 
170
- Thread.current[:bullet_object_associations] = nil
171
- Thread.current[:bullet_call_object_associations] = nil
172
- Thread.current[:bullet_possible_objects] = nil
173
- Thread.current[:bullet_impossible_objects] = nil
174
- Thread.current[:bullet_inversed_objects] = nil
175
- Thread.current[:bullet_eager_loadings] = nil
176
+ Thread.current.thread_variable_set(:bullet_object_associations, nil)
177
+ Thread.current.thread_variable_set(:bullet_call_object_associations, nil)
178
+ Thread.current.thread_variable_set(:bullet_possible_objects, nil)
179
+ Thread.current.thread_variable_set(:bullet_impossible_objects, nil)
180
+ Thread.current.thread_variable_set(:bullet_inversed_objects, nil)
181
+ Thread.current.thread_variable_set(:bullet_eager_loadings, nil)
176
182
 
177
- Thread.current[:bullet_counter_possible_objects] = nil
178
- Thread.current[:bullet_counter_impossible_objects] = nil
183
+ Thread.current.thread_variable_set(:bullet_counter_possible_objects, nil)
184
+ Thread.current.thread_variable_set(:bullet_counter_impossible_objects, nil)
179
185
  end
180
186
 
181
187
  def start?
182
- enable? && Thread.current[:bullet_start]
188
+ enable? && Thread.current.thread_variable_get(:bullet_start)
183
189
  end
184
190
 
185
191
  def notification_collector
186
- Thread.current[:bullet_notification_collector]
192
+ Thread.current.thread_variable_get(:bullet_notification_collector)
187
193
  end
188
194
 
189
195
  def notification?