Class: Skadi::ControllerDelegate
- Inherits:
-
Object
- Object
- Skadi::ControllerDelegate
- Defined in:
- lib/skadi/controller_delegate.rb
Instance Attribute Summary collapse
-
#controller ⇒ ActionController::Base
readonly
The controller that instantiated us.
-
#demographics ⇒ Object
readonly
Returns the value of attribute demographics.
-
#events ⇒ Object
readonly
Returns the value of attribute events.
-
#view ⇒ Object
readonly
Returns the value of attribute view.
-
#visit ⇒ Object
readonly
Returns the value of attribute visit.
Instance Method Summary collapse
-
#_attach(view: nil, visit: nil) ⇒ Object
Internal.
-
#_persist ⇒ Object
Internal.
-
#_prepare ⇒ Object
Internal.
-
#anonymity_set_consent!(consent) ⇒ Object
Set consent for tracking by anonymity set.
- #anonymity_set_consent? ⇒ Boolean
-
#cookie_consent!(consent) ⇒ Object
Set consent for tracking by cookie.
- #cookie_consent? ⇒ Boolean
-
#demographic(name, value, action_specific: false, uri: nil) ⇒ Object
Create or increment a demographic with a given name or value.
-
#do_not_track! ⇒ Object
Disable tracking for the current request.
-
#do_not_track? ⇒ Boolean
Whether Skadi tracking has been disabled for the current request.
-
#event(name, properties = {}, sensitive: false) ⇒ Object
Create an event with the given name and properties.
-
#initialize(controller, bot_protection: true) ⇒ ControllerDelegate
constructor
A new instance of ControllerDelegate.
-
#new_visit? ⇒ TrueClass, FalseClass
Whether the current request has recorded a new visit.
-
#user_agent ⇒ Skadi::UserAgent
The parsed user agent.
-
#user_consent!(consent) ⇒ Object
Set consent for tracking by logged in user.
- #user_consent? ⇒ Boolean
Constructor Details
#initialize(controller, bot_protection: true) ⇒ ControllerDelegate
Returns a new instance of ControllerDelegate.
26 27 28 29 30 31 32 33 34 |
# File 'lib/skadi/controller_delegate.rb', line 26 def initialize(controller, bot_protection: true) @controller = controller @events = [] @demographics = [] @do_not_track = bot_protection && Skadi.configuration.do_not_track_bots? && user_agent.bot? @new_visit = false end |
Instance Attribute Details
#controller ⇒ ActionController::Base (readonly)
The controller that instantiated us
5 6 7 |
# File 'lib/skadi/controller_delegate.rb', line 5 def controller @controller end |
#demographics ⇒ Object (readonly)
Returns the value of attribute demographics.
7 8 9 |
# File 'lib/skadi/controller_delegate.rb', line 7 def demographics @demographics end |
#events ⇒ Object (readonly)
Returns the value of attribute events.
7 8 9 |
# File 'lib/skadi/controller_delegate.rb', line 7 def events @events end |
#view ⇒ Object (readonly)
Returns the value of attribute view.
7 8 9 |
# File 'lib/skadi/controller_delegate.rb', line 7 def view @view end |
#visit ⇒ Object (readonly)
Returns the value of attribute visit.
7 8 9 |
# File 'lib/skadi/controller_delegate.rb', line 7 def visit @visit end |
Instance Method Details
#_attach(view: nil, visit: nil) ⇒ Object
Internal. Manually set the view and visit for the current request.
53 54 55 56 |
# File 'lib/skadi/controller_delegate.rb', line 53 def _attach(view: nil, visit: nil) @visit = visit @view = view end |
#_persist ⇒ Object
Internal. Saves the visit, view and any events or demographics after the controller action.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/skadi/controller_delegate.rb', line 59 def _persist return if do_not_track? @visit&.save if @view @view.visit = @visit @view.save end if @events.any? Skadi::Event.redact_and_insert(@events, visit: @visit, view: @view) end if @demographics.any? Demographic.create_or_increment_all(@demographics) end .renew! rescue => e # Analytics must not interfere with the host app's request on failure Rails.logger.error("Skadi: failed to persist analytics for #{controller.controller_name}##{controller.action_name} (visit: #{@visit.try(:id).inspect}, view: #{@view.try(:id).inspect}, events: #{@events.count}, demographics: #{@demographics.count}): #{e.class}, #{e.}; Line: #{e.backtrace&.first}") # Ensure errors are visible in test and development raise if Rails.env.local? end |
#_prepare ⇒ Object
Internal. Performs the before-action tasks: build the visit and view, and process user agent info.
38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/skadi/controller_delegate.rb', line 38 def _prepare return if do_not_track? build_visit build_view queue_user_agent_demographics rescue => e # Analytics must not interfere with the host app's request on failure Rails.logger.error("Skadi: failed to prepare analytics for #{controller.controller_name}##{controller.action_name} (visit: #{@visit.try(:id).inspect}, view: #{@view.try(:id).inspect}, events: #{@events.count}, demographics: #{@demographics.count}): #{e.class}, #{e.}; Line: #{e.backtrace&.first}") # Ensure errors are visible in test and development raise if Rails.env.local? end |
#anonymity_set_consent!(consent) ⇒ Object
Set consent for tracking by anonymity set
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/skadi/controller_delegate.rb', line 136 def () anonymity_set = AnonymitySet.calculate(request.remote_ip, request.user_agent) if .use_anonymity_sets = true # If a visit is attached to the request, we update it with the anonymity set token if @visit @visit.tracking_token ||= anonymity_set else # Build the visit without the request, because the current request is likely not the original first request @visit = Visit.build_from(anonymity_set) @view.visit = @visit if @view end else .use_anonymity_sets = false return if @visit.nil? # Check to see if the currrent visit is using an anonymity set if @visit&.tracking_token && !@visit. # If so, delete it from the db so existing data is anonymised instantly # Note: this needs to be a DB update because there may be other visits outside the visit limit Skadi::Visit.where(tracking_token: anonymity_set).update_all(tracking_token: nil) # Update the local instance of the visit if it uses anonymity sets so it doesn't get re-set when saved @visit.tracking_token = nil if @visit.tracking_token == anonymity_set end end end |
#anonymity_set_consent? ⇒ Boolean
167 168 169 170 171 172 173 |
# File 'lib/skadi/controller_delegate.rb', line 167 def = .use_anonymity_sets return unless .nil? # There is no explicit consent or opt-out, so we use the configured default value return Skadi.configuration.use_anonymity_sets end |
#cookie_consent!(consent) ⇒ Object
Set consent for tracking by cookie
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/skadi/controller_delegate.rb', line 177 def () if return unless .tracking_token.nil? # Re-use an existing cookie-based token tracking_token = @visit&.tracking_token if @visit&. tracking_token ||= ::SecureRandom.uuid_v7 .tracking_token = tracking_token # Update the existing visit with the tracking token if we've generated a new one if @visit @visit.tracking_token = tracking_token @visit. = true else @visit = Visit.build_from(tracking_token, cookies_enabled: true) @view.visit = @visit if @view end else .tracking_token = nil return if @visit.nil? # No need to anonymise existing sessions here because there is no way to link to the user once the tracking token is deleted. @visit. = false # If the user has opted in for anonymity sets if .use_anonymity_sets == true || (Skadi.configuration.use_anonymity_sets && .use_anonymity_sets != false) @visit.tracking_token = AnonymitySet.calculate(request.remote_ip, request.user_agent) end end end |
#cookie_consent? ⇒ Boolean
210 |
# File 'lib/skadi/controller_delegate.rb', line 210 def = .tracking_token.present? |
#demographic(name, value, action_specific: false, uri: nil) ⇒ Object
Create or increment a demographic with a given name or value. If the action_specific parameter is set to true, the demographic is linked specifically to the current action. Demographics are not linked to any other individual data. E.g:
skadi.demographic("browser", "Chrome")
skadi.demographic("branch", "A", action_specific: true)
If the name/value/action combination doesn't exist for the current date, a new row is added to the database with count set to 1. If it does, the existing record's count is incremented.
109 110 111 112 113 114 115 116 |
# File 'lib/skadi/controller_delegate.rb', line 109 def demographic(name, value, action_specific: false, uri: nil) raise ArgumentError.new "Skadi::ControllerDelegate.demographic expects String as first parameter, got #{name.is_a?(String) ? "empty string" : name.class.name}" unless name.is_a?(String) && name.present? raise ArgumentError.new "Skadi::ControllerDelegate.demographic expects String as second parameter, got #{value.is_a?(String) ? "empty string" : value.class.name}" unless value.is_a?(String) && value.present? demographic = { name:, value:, uri: action_specific ? (uri || request.route_uri_pattern) : nil } @demographics << demographic end |
#do_not_track! ⇒ Object
Disable tracking for the current request
92 93 94 |
# File 'lib/skadi/controller_delegate.rb', line 92 def do_not_track! @do_not_track = true end |
#do_not_track? ⇒ Boolean
Whether Skadi tracking has been disabled for the current request
87 88 89 |
# File 'lib/skadi/controller_delegate.rb', line 87 def do_not_track? @do_not_track end |
#event(name, properties = {}, sensitive: false) ⇒ Object
Create an event with the given name and properties. By default, events are linked to the current visit and view, but if the sensitive parameter is set to true, the event is not linked to the visit and view, and the time of the event is set to the start of the current day.
125 126 127 128 129 130 131 132 |
# File 'lib/skadi/controller_delegate.rb', line 125 def event(name, properties = {}, sensitive: false) raise ArgumentError.new "Skadi::ControllerDelegate.event expects String as first parameter, got #{name.is_a?(String) ? "empty string" : name.class.name}" unless name.is_a?(String) && name.present? raise ArgumentError.new "Skadi::ControllerDelegate.event expects Hash as second parameter, got #{properties.class.name}" unless properties.is_a?(Hash) event = { name:, properties:, sensitive: } @events << event end |
#new_visit? ⇒ TrueClass, FalseClass
Whether the current request has recorded a new visit
15 |
# File 'lib/skadi/controller_delegate.rb', line 15 def new_visit? = @new_visit |
#user_agent ⇒ Skadi::UserAgent
The parsed user agent
11 |
# File 'lib/skadi/controller_delegate.rb', line 11 def user_agent = @_user_agent ||= UserAgent.new(request.user_agent || "") |
#user_consent!(consent) ⇒ Object
Set consent for tracking by logged in user
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/skadi/controller_delegate.rb', line 214 def () tracked_user_id = @visit&.user_id || logged_in_user&.id if .track_users = true unless tracked_user_id.nil? if @visit @visit.user_id = tracked_user_id else # Build the visit without the request, because the current request is likely not the original first request @visit = Visit.build_from(nil, tracked_user_id) @view.visit = @visit if @view end end else .track_users = false # If there is a logged in user, we delete the user id from any rows that match unless tracked_user_id.nil? # Note: this needs a DB update because there may be other visits outside the visit limit Skadi::Visit.where(user_id: tracked_user_id).update_all(user_id: nil) # Update the local instance of the current visit so it doesn't get re-set when saved @visit.user_id = nil if @visit end end end |
#user_consent? ⇒ Boolean
243 244 245 246 247 248 249 |
# File 'lib/skadi/controller_delegate.rb', line 243 def = .track_users return unless .nil? # There is no explicit consent or opt-out, so we use the configured default value return Skadi.configuration.track_users end |