rox-rollout 5.0.3 → 5.1.1

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/e2e_tests.yaml +52 -0
  3. data/.github/workflows/{ruby.yml → unit_tests.yaml} +4 -5
  4. data/.gitignore +1 -0
  5. data/Rakefile +0 -6
  6. data/e2e-server/.gitignore +1 -0
  7. data/e2e-server/run_server.sh +7 -3
  8. data/e2e-server/server.rb +26 -56
  9. data/example/local.rb +7 -7
  10. data/lib/rox/core/analytics/backoff_policy.rb +51 -0
  11. data/lib/rox/core/analytics/client.rb +187 -0
  12. data/lib/rox/core/analytics/defaults.rb +31 -0
  13. data/lib/rox/core/analytics/logging.rb +62 -0
  14. data/lib/rox/core/analytics/message_batch.rb +74 -0
  15. data/lib/rox/core/analytics/response.rb +17 -0
  16. data/lib/rox/core/analytics/test_queue.rb +58 -0
  17. data/lib/rox/core/analytics/transport.rb +144 -0
  18. data/lib/rox/core/analytics/utils.rb +89 -0
  19. data/lib/rox/core/analytics/worker.rb +67 -0
  20. data/lib/rox/core/consts/environment.rb +62 -54
  21. data/lib/rox/core/core.rb +19 -9
  22. data/lib/rox/core/entities/flag.rb +3 -1
  23. data/lib/rox/core/entities/rox_double.rb +3 -1
  24. data/lib/rox/core/entities/rox_int.rb +3 -1
  25. data/lib/rox/core/entities/rox_string.rb +3 -2
  26. data/lib/rox/core/impression/impression_invoker.rb +4 -6
  27. data/lib/rox/core/network/request_configuration_builder.rb +1 -1
  28. data/lib/rox/core/network/response.rb +2 -2
  29. data/lib/rox/core/network/state_sender.rb +1 -1
  30. data/lib/rox/server/network_configurations_options.rb +20 -0
  31. data/lib/rox/server/rox_options.rb +3 -9
  32. data/lib/rox/server/self_managed_options.rb +12 -0
  33. data/lib/rox/version.rb +1 -1
  34. data/rox.gemspec +2 -2
  35. metadata +25 -16
  36. data/.circleci/config.yml +0 -73
  37. data/e2e/container.rb +0 -35
  38. data/e2e/custom_props.rb +0 -55
  39. data/e2e/rox_e2e_test.rb +0 -157
  40. data/e2e/test_vars.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d30a63b44e459ce5e10a2b64ce89e3ee59b59ceea67969fda6a1f5b962e43e4
4
- data.tar.gz: 2bc7318f4e7476830c09fc4c9cf3fbcb9b8e21c0ad6bfdc34ed4e74d321eb8a6
3
+ metadata.gz: ae34336f9f49d2ba7a2f77003d3f537eb53bbf4cde40fff04846a4c71d9fe177
4
+ data.tar.gz: d168174f3d6b72a482b89868d950e4bd8c30c908c0df6d0c11d276aceb8c229d
5
5
  SHA512:
6
- metadata.gz: 2323d41086d92479bdafae351c9aabce8ad99ec4b870b30fb72ada7a8783bf211dbe6380fe80590d79fdd90d077e935d8961dea30436f3211c37cf2a52bb8da6
7
- data.tar.gz: e3c8ff2bb1018d0d2db497fdbd307a4cac69edee35823f8b35adf47168bfa3339f3ef03401f864f4b918ee83c823e423aa472b44da67ca918f78fcdee02eca53
6
+ metadata.gz: be4f8985f0289f5becb88a46f13578d83982ad0b5d471c3241dd8127493cdca9d1479eae63bc87e6c13c81092a37ae63e48bedaa40b806020a497a091e0996f2
7
+ data.tar.gz: f5c134a9692176e0495bbb3805799a439c14a08b5c160a61148c4affdb7e39f2699d1586e7f853e6bdd7fc43103bd9a66d17fc61b93a9b228627a92a7a6217ba
@@ -0,0 +1,52 @@
1
+ name: E2E tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+
11
+ jobs:
12
+ build:
13
+
14
+ runs-on: macos-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@v3
18
+ with:
19
+ path: rox-ruby
20
+ - name: Checkout e2e tests
21
+ uses: actions/checkout@v3
22
+ with:
23
+ repository: rollout/sdk-end-2-end-tests
24
+ ref: master
25
+ token: ${{ secrets.E2E_PAT }}
26
+ path: sdk-end-2-end-tests
27
+ - name: link driver
28
+ working-directory: ./sdk-end-2-end-tests/drivers
29
+ run: ln -s $GITHUB_WORKSPACE/rox-ruby/e2e-server ruby
30
+ - name: build e2e node driver
31
+ working-directory: ./sdk-end-2-end-tests/drivers/nodejs
32
+ run: |
33
+ yarn install --frozen-lockfile
34
+ - name: build and run e2e
35
+ working-directory: ./sdk-end-2-end-tests
36
+ run: |
37
+ yarn install --frozen-lockfile
38
+ QA_E2E_BEARER=$QA_E2E_BEARER API_HOST=https://api.test.rollout.io CD_API_ENDPOINT=https://api.test.rollout.io/device/get_configuration CD_S3_ENDPOINT=https://rox-conf.test.rollout.io/ SS_API_ENDPOINT=https://api.test.rollout.io/device/update_state_store/ SS_S3_ENDPOINT=https://rox-state.test.rollout.io/ CLIENT_DATA_CACHE_KEY=client_data ANALYTICS_ENDPOINT=https://analytic.test.rollout.io/ NOTIFICATIONS_ENDPOINT=https://push.test.rollout.io/sse SDK_LANG=ruby NODE_ENV=container yarn test:env
39
+ env:
40
+ QA_E2E_BEARER: ${{ secrets.QA_E2E_BEARER }}
41
+ - name: Show e2e server driver logs
42
+ if: ${{ always() }}
43
+ run: cat ./sdk-end-2-end-tests/drivers/ruby/log_1234.out || echo "no log file"
44
+ - name: Show e2e server sync driver logs
45
+ if: ${{ always() }}
46
+ run: cat ./sdk-end-2-end-tests/drivers/nodejs/log_1233.out || echo "no log file"
47
+ - name: Show e2e server init ClientData driver logs
48
+ if: ${{ always() }}
49
+ run: cat ./sdk-end-2-end-tests/drivers/ruby/log_2234.out || echo "no log file"
50
+ - name: Show e2e server init ClientData sync driver logs
51
+ if: ${{ always() }}
52
+ run: cat ./sdk-end-2-end-tests/drivers/nodejs/log_2233.out || echo "no log file"
@@ -1,4 +1,4 @@
1
- name: Ruby
1
+ name: Unit Tests
2
2
 
3
3
  on: [push]
4
4
 
@@ -8,14 +8,13 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  os: [ubuntu-latest]
11
- ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1]
11
+ ruby: [2.6, 2.7, '3.0', 3.1, 3.2]
12
12
  runs-on: ${{ matrix.os }}
13
13
  steps:
14
- - uses: actions/checkout@v2
14
+ - uses: actions/checkout@v3
15
15
  - uses: ruby/setup-ruby@v1
16
16
  with:
17
17
  ruby-version: ${{ matrix.ruby }}
18
- bundler: 2.3.3
18
+ bundler: 2.4.6
19
19
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
20
20
  - run: bundle exec rake test
21
- - run: bundle exec rake e2e
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ /.idea/
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /_yardoc/
data/Rakefile CHANGED
@@ -8,12 +8,6 @@ Rake::TestTask.new(:test) do |t|
8
8
  t.test_files = FileList['test/**/*_test.rb']
9
9
  end
10
10
 
11
- Rake::TestTask.new(:e2e) do |t|
12
- t.libs << 'lib'
13
- t.libs << 'e2e'
14
- t.test_files = FileList['e2e/**/*_test.rb']
15
- end
16
-
17
11
  RuboCop::RakeTask.new(:lint)
18
12
 
19
13
  task default: :test
@@ -0,0 +1 @@
1
+ *.out
@@ -2,9 +2,13 @@
2
2
  DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
3
3
  cd -P "$DIR"
4
4
  DIR=`pwd`
5
- gem install sinatra
6
- gem install sinatra-contrib
7
- gem install json
5
+
6
+ # --conservative will save us some time (hopefully gem install just once)
7
+ gem install sinatra --conservative
8
+ gem install sinatra-contrib --conservative
9
+ gem install json --conservative
10
+ gem install em-eventsource --conservative
11
+
8
12
  nohup ruby ./server.rb $1 1>"$DIR"/log_"$1".out 2>&1 &
9
13
  while true ; do
10
14
  curl -p http://127.0.0.1:$1/status-check && exit
data/e2e-server/server.rb CHANGED
@@ -1,18 +1,20 @@
1
+ # comment the following line and install a rox-ruby gem to test a released version
2
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
3
+
1
4
  require 'sinatra'
2
5
  require 'sinatra/namespace'
3
6
  require 'json'
4
7
  require 'rox/server/flags/rox_flag'
5
8
  require 'rox/server/rox_server'
6
9
  require 'rox/server/rox_options'
10
+ require 'rox/server/network_configurations_options'
7
11
 
8
12
  class Container
9
- attr_accessor :first_flag
10
- attr_reader :bool_default_false, :bool_default_true
13
+ attr_reader :boolDefaultFalse, :boolDefaultTrue
11
14
 
12
15
  def initialize
13
- @bool_default_false = Rox::Server::RoxFlag.new(false)
14
- @bool_default_true = Rox::Server::RoxFlag.new(true)
15
- @first_flag = Rox::Server::RoxFlag.new(true)
16
+ @boolDefaultFalse = Rox::Server::RoxFlag.new(false)
17
+ @boolDefaultTrue = Rox::Server::RoxFlag.new(true)
16
18
  end
17
19
  end
18
20
 
@@ -58,95 +60,63 @@ namespace '/' do
58
60
  [200, '']
59
61
  end
60
62
 
61
- get 'api/values/:id' do |id|
62
- if container.first_flag.enabled?
63
- [200, JSON.generate({ value: id })]
64
- else
65
- [200, 'Eladddddd']
66
- end
67
- end
68
-
69
- put 'api/values/:id' do
70
- [200, '']
71
- end
72
-
73
- delete 'api/values/:id' do
74
- [200, '']
75
- end
76
-
77
63
  post '' do
78
64
  data = JSON.parse(request.body.read)
79
65
  action = data['action']
80
66
  payload = data['payload']
81
- puts data
82
-
67
+ puts "data => #{data}"
83
68
  case action
84
69
  when 'staticFlagIsEnabled'
85
70
  flag = payload['flag']
86
71
  result = container.send(flag.to_sym).enabled?(payload['context'])
87
72
  [200, JSON.generate({ result: result })]
88
73
  when 'registerStaticContainers'
89
- Rox::Server::RoxServer.register(container)
74
+ Rox::Server::RoxServer.register_with_namespace('namespace', container)
90
75
  [200, JSON.generate({ result: 'done' })]
91
76
  when 'setCustomPropertyToThrow'
92
77
  def raise_(ex)
93
78
  raise ex
94
79
  end
95
- Rox::Server::RoxServer.set_custom_string_property(payload['key'], raise_(Exception('error')))
80
+ Rox::Server::RoxServer.set_custom_string_property(payload['key']) do |_context|
81
+ raise_(StandardError.new "custom property generator error")
82
+ end
96
83
  [200, JSON.generate({ result: 'done' })]
97
84
  when 'setCustomStringProperty'
98
85
  Rox::Server::RoxServer.set_custom_string_property(payload['key'], payload['value'])
99
86
  [200, JSON.generate({ result: 'done' })]
100
87
  when 'dynamicFlagValue'
101
- if payload['context']
102
- puts "There is a payload[context] => #{payload['context']}"
103
- payload['context'].each do |context|
104
- key = context[0]
105
- value = context[1]
106
- Rox::Server::RoxServer.set_custom_string_property(key, value) if value.instance_of? String
107
- if value.instance_of? TrueClass
108
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
109
- elsif value.instance_of? FalseClass
110
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
111
- end
112
- Rox::Server::RoxServer.set_custom_int_property(key, value) if value.instance_of? Integer
113
- Rox::Server::RoxServer.set_custom_float_property(key, value) if value.instance_of? Float
114
- end
115
- end
116
- result = Rox::Server::RoxServer.dynamic_api.value(payload['flag'], payload['defaultValue'], payload['context'],
117
- [])
88
+ result = Rox::Server::RoxServer.dynamic_api.value(payload['flag'], payload['defaultValue'], payload['context'], [])
118
89
  [200, JSON.generate({ result: result })]
119
90
  when 'dynamicFlagIsEnabled'
120
- payload['context']&.each do |context|
121
- key = context[0]
122
- value = context[1]
123
- Rox::Server::RoxServer.set_custom_string_property(key, value) if value.instance_of? String
124
- if value.instance_of? TrueClass
125
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
126
- elsif value.instance_of? FalseClass
127
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
128
- end
129
- Rox::Server::RoxServer.set_custom_int_property(key, value) if value.instance_of? Integer
130
- Rox::Server::RoxServer.set_custom_float_property(key, value) if value.instance_of? Float
131
- end
132
91
  result = Rox::Server::RoxServer.dynamic_api.enabled?(payload['flag'], payload['defaultValue'], payload['context'])
133
92
  [200, JSON.generate({ result: result })]
134
93
  when 'setupAndAwait'
135
94
  env = 'stam'
95
+ fetch_interval = nil
136
96
  if payload['options']
137
97
  options = payload['options']
98
+ env = options['env']
99
+ fetch_interval = options['fetchInterval']
138
100
  if options['configuration']
139
101
  configuration = options['configuration']
140
- env = configuration['env']
141
102
  end
142
103
  end
104
+ network_config = nil
143
105
  case env
106
+ when 'container'
107
+ network_config = Rox::Server::NetworkConfigurationsOptions.new(
108
+ get_config_api_endpoint: configuration['CD_API_ENDPOINT']&.chomp('/'),
109
+ get_config_cloud_endpoint: configuration['CD_S3_ENDPOINT']&.chomp('/'),
110
+ send_state_api_endpoint: configuration['SS_API_ENDPOINT']&.chomp('/'),
111
+ send_state_cloud_endpoint: configuration['SS_S3_ENDPOINT']&.chomp('/'),
112
+ analytics_endpoint: configuration['ANALYTICS_ENDPOINT']&.chomp('/'),
113
+ push_notification_endpoint: configuration['NOTIFICATIONS_ENDPOINT']&.chomp('/'))
144
114
  when 'qa'
145
115
  ENV['ROLLOUT_MODE'] = 'QA'
146
116
  when 'localhost'
147
117
  ENV['ROLLOUT_MODE'] = 'LOCAL'
148
118
  end
149
- options = Rox::Server::RoxOptions.new(logger: ServerLogger.new)
119
+ options = Rox::Server::RoxOptions.new(fetch_interval: fetch_interval,logger: ServerLogger.new, network_configurations_options: network_config)
150
120
  puts "options => #{options}"
151
121
  Rox::Server::RoxServer.setup(payload['key'], options).value
152
122
  [200, JSON.generate({ result: 'done' })]
data/example/local.rb CHANGED
@@ -4,8 +4,8 @@ require 'rox/server/rox_server'
4
4
  require 'rox/server/rox_options'
5
5
 
6
6
  API_HOST = 'http://localhost:8557'.freeze
7
- APP_KEY = '611e70975d05440313ccda68'.freeze
8
- #DEV_MODE_SECRET = 'e56cda16749d8d0a9b91d34c'.freeze
7
+ APP_KEY = '600571e330819d4842999e4f'.freeze
8
+ DEV_MODE_SECRET = 'e56cda16749d8d0a9b91d34c'.freeze
9
9
 
10
10
  class Flags
11
11
  attr_accessor :boolean_flag, :string_flag, :int_flag, :double_flag
@@ -23,11 +23,11 @@ flags = Flags.new
23
23
  Rox::Server::RoxServer.register(flags)
24
24
 
25
25
  options = Rox::Server::RoxOptions.new(
26
- #self_managed_options: Rox::Server::SelfManagedOptions.new(
27
- # server_url: API_HOST,
28
- # analytics_url: 'http://127.0.0.1:8787'
29
- #),
30
- #dev_mode_key: DEV_MODE_SECRET
26
+ self_managed_options: Rox::Server::SelfManagedOptions.new(
27
+ server_url: API_HOST,
28
+ analytics_url: 'http://127.0.0.1:8787'
29
+ ),
30
+ dev_mode_key: DEV_MODE_SECRET
31
31
  )
32
32
 
33
33
  Rox::Server::RoxServer.setup(APP_KEY, options)
@@ -0,0 +1,51 @@
1
+ require 'rox/core/analytics/defaults'
2
+
3
+ module Rox
4
+ module Core
5
+ class Analytics
6
+ class BackoffPolicy
7
+ include Rox::Core::Analytics::Defaults::BackoffPolicy
8
+
9
+ # @param [Hash] opts
10
+ # @option opts [Numeric] :min_timeout_ms The minimum backoff timeout
11
+ # @option opts [Numeric] :max_timeout_ms The maximum backoff timeout
12
+ # @option opts [Numeric] :multiplier The value to multiply the current
13
+ # interval with for each retry attempt
14
+ # @option opts [Numeric] :randomization_factor The randomization factor
15
+ # to use to create a range around the retry interval
16
+ def initialize(opts = {})
17
+ @min_timeout_ms = opts[:min_timeout_ms] || MIN_TIMEOUT_MS
18
+ @max_timeout_ms = opts[:max_timeout_ms] || MAX_TIMEOUT_MS
19
+ @multiplier = opts[:multiplier] || MULTIPLIER
20
+ @randomization_factor = opts[:randomization_factor] || RANDOMIZATION_FACTOR
21
+
22
+ @attempts = 0
23
+ end
24
+
25
+ # @return [Numeric] the next backoff interval, in milliseconds.
26
+ def next_interval
27
+ interval = @min_timeout_ms * (@multiplier ** @attempts)
28
+ interval = add_jitter(interval, @randomization_factor)
29
+
30
+ @attempts += 1
31
+
32
+ [interval, @max_timeout_ms].min
33
+ end
34
+
35
+ private
36
+
37
+ def add_jitter(base, randomization_factor)
38
+ random_number = rand
39
+ max_deviation = base * randomization_factor
40
+ deviation = random_number * max_deviation
41
+
42
+ if random_number < 0.5
43
+ base - deviation
44
+ else
45
+ base + deviation
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,187 @@
1
+ require 'thread'
2
+ require 'time'
3
+ require 'uri'
4
+
5
+ require 'rox/core/analytics/defaults'
6
+ require 'rox/core/analytics/logging'
7
+ require 'rox/core/analytics/utils'
8
+ require 'rox/core/analytics/worker'
9
+
10
+ module Rox
11
+ module Core
12
+ class Analytics
13
+ class Client
14
+ include Rox::Core::Analytics::Utils
15
+ include Rox::Core::Analytics::Logging
16
+
17
+ # @param [Rox::Core::DeviceProperties] device_properties
18
+ def initialize(device_properties)
19
+ @queue = Queue.new
20
+ @max_queue_size = Defaults::Queue::MAX_SIZE
21
+ @worker_mutex = Mutex.new
22
+ @worker = Worker.new(@queue, device_properties)
23
+ @worker_thread = nil
24
+
25
+ at_exit { @worker_thread && @worker_thread[:should_exit] = true }
26
+ end
27
+
28
+ # Synchronously waits until the worker has flushed the queue.
29
+ #
30
+ # Use only for scripts which are not long-running, and will specifically
31
+ # exit
32
+ def flush
33
+ while !@queue.empty? || @worker.is_requesting?
34
+ ensure_worker_running
35
+ sleep(0.1)
36
+ end
37
+ end
38
+
39
+ # @!macro common_attrs
40
+ # @option attrs [String] :anonymous_id ID for a user when you don't know
41
+ # who they are yet. (optional but you must provide either an
42
+ # `anonymous_id` or `user_id`)
43
+ # @option attrs [Hash] :context ({})
44
+ # @option attrs [Hash] :integrations What integrations this event
45
+ # goes to (optional)
46
+ # @option attrs [String] :message_id ID that uniquely
47
+ # identifies a message across the API. (optional)
48
+ # @option attrs [Time] :timestamp When the event occurred (optional)
49
+ # @option attrs [String] :user_id The ID for this user in your database
50
+ # (optional but you must provide either an `anonymous_id` or `user_id`)
51
+ # @option attrs [Hash] :options Options such as user traits (optional)
52
+
53
+ # Tracks an event
54
+ #
55
+ # @see https://segment.com/docs/sources/server/ruby/#track
56
+ #
57
+ # @param [Hash] attrs
58
+ #
59
+ # @option attrs [String] :event Event name
60
+ # @option attrs [Hash] :properties Event properties (optional)
61
+ # @macro common_attrs
62
+ def track(attrs)
63
+ symbolize_keys! attrs
64
+ enqueue(attrs)
65
+ end
66
+
67
+ # Identifies a user
68
+ #
69
+ # @see https://segment.com/docs/sources/server/ruby/#identify
70
+ #
71
+ # @param [Hash] attrs
72
+ #
73
+ # @option attrs [Hash] :traits User traits (optional)
74
+ # @macro common_attrs
75
+ def identify(attrs)
76
+ symbolize_keys! attrs
77
+ enqueue(FieldParser.parse_for_identify(attrs))
78
+ end
79
+
80
+ # Aliases a user from one id to another
81
+ #
82
+ # @see https://segment.com/docs/sources/server/ruby/#alias
83
+ #
84
+ # @param [Hash] attrs
85
+ #
86
+ # @option attrs [String] :previous_id The ID to alias from
87
+ # @macro common_attrs
88
+ def alias(attrs)
89
+ symbolize_keys! attrs
90
+ enqueue(FieldParser.parse_for_alias(attrs))
91
+ end
92
+
93
+ # Associates a user identity with a group.
94
+ #
95
+ # @see https://segment.com/docs/sources/server/ruby/#group
96
+ #
97
+ # @param [Hash] attrs
98
+ #
99
+ # @option attrs [String] :group_id The ID of the group
100
+ # @option attrs [Hash] :traits User traits (optional)
101
+ # @macro common_attrs
102
+ def group(attrs)
103
+ symbolize_keys! attrs
104
+ enqueue(FieldParser.parse_for_group(attrs))
105
+ end
106
+
107
+ # Records a page view
108
+ #
109
+ # @see https://segment.com/docs/sources/server/ruby/#page
110
+ #
111
+ # @param [Hash] attrs
112
+ #
113
+ # @option attrs [String] :name Name of the page
114
+ # @option attrs [Hash] :properties Page properties (optional)
115
+ # @macro common_attrs
116
+ def page(attrs)
117
+ symbolize_keys! attrs
118
+ enqueue(FieldParser.parse_for_page(attrs))
119
+ end
120
+
121
+ # Records a screen view (for a mobile app)
122
+ #
123
+ # @param [Hash] attrs
124
+ #
125
+ # @option attrs [String] :name Name of the screen
126
+ # @option attrs [Hash] :properties Screen properties (optional)
127
+ # @option attrs [String] :category The screen category (optional)
128
+ # @macro common_attrs
129
+ def screen(attrs)
130
+ symbolize_keys! attrs
131
+ enqueue(FieldParser.parse_for_screen(attrs))
132
+ end
133
+
134
+ # @return [Fixnum] number of messages in the queue
135
+ def queued_messages
136
+ @queue.length
137
+ end
138
+
139
+ def test_queue
140
+ unless @test
141
+ raise 'Test queue only available when setting :test to true.'
142
+ end
143
+
144
+ @test_queue ||= TestQueue.new
145
+ end
146
+
147
+ private
148
+
149
+ # private: Enqueues the action.
150
+ #
151
+ # returns Boolean of whether the item was added to the queue.
152
+ def enqueue(action)
153
+
154
+ if @test
155
+ test_queue << action
156
+ return true
157
+ end
158
+
159
+ while @queue.length >= @max_queue_size
160
+ # remove the oldest impression,
161
+ # and then add the new one (otherwise it just rejects the newer one)
162
+ @queue.pop
163
+ end
164
+
165
+ @queue << action
166
+ ensure_worker_running
167
+
168
+ true
169
+ end
170
+
171
+ def ensure_worker_running
172
+ return if worker_running?
173
+ @worker_mutex.synchronize do
174
+ return if worker_running?
175
+ @worker_thread = Thread.new do
176
+ @worker.run
177
+ end
178
+ end
179
+ end
180
+
181
+ def worker_running?
182
+ @worker_thread && @worker_thread.alive?
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,31 @@
1
+ module Rox
2
+ module Core
3
+ class Analytics
4
+ module Defaults
5
+ module Request
6
+ RETRIES = 10
7
+ end
8
+
9
+ module Queue
10
+ MAX_SIZE = 10000
11
+ end
12
+
13
+ module Message
14
+ MAX_BYTES = 32768 # 32Kb
15
+ end
16
+
17
+ module MessageBatch
18
+ MAX_BYTES = 512_000 # 500Kb
19
+ MAX_SIZE = 100
20
+ end
21
+
22
+ module BackoffPolicy
23
+ MIN_TIMEOUT_MS = 100
24
+ MAX_TIMEOUT_MS = 10000
25
+ MULTIPLIER = 1.5
26
+ RANDOMIZATION_FACTOR = 0.5
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,62 @@
1
+ require 'logger'
2
+
3
+ module Rox
4
+ module Core
5
+ class Analytics
6
+ # Wraps an existing logger and adds a prefix to all messages
7
+ class PrefixedLogger
8
+ def initialize(logger, prefix)
9
+ @logger = logger
10
+ @prefix = prefix
11
+ end
12
+
13
+ def debug(msg)
14
+ @logger.debug("#{@prefix} #{msg}")
15
+ end
16
+
17
+ def info(msg)
18
+ @logger.info("#{@prefix} #{msg}")
19
+ end
20
+
21
+ def warn(msg)
22
+ @logger.warn("#{@prefix} #{msg}")
23
+ end
24
+
25
+ def error(msg)
26
+ @logger.error("#{@prefix} #{msg}")
27
+ end
28
+ end
29
+
30
+ module Logging
31
+ class << self
32
+ def logger
33
+ return @logger if @logger
34
+
35
+ base_logger = if defined?(Rails)
36
+ Rails.logger
37
+ else
38
+ logger = Logger.new STDOUT
39
+ logger.progname = 'Rox::Core::Analytics'
40
+ logger
41
+ end
42
+ @logger = PrefixedLogger.new(base_logger, '[analytics-ruby]')
43
+ end
44
+
45
+ attr_writer :logger
46
+ end
47
+
48
+ def self.included(base)
49
+ class << base
50
+ def logger
51
+ Logging.logger
52
+ end
53
+ end
54
+ end
55
+
56
+ def logger
57
+ Logging.logger
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end