mixpanel-ruby 2.3.0 → 3.0.0
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/.github/workflows/ruby.yml +37 -0
- data/Readme.rdoc +8 -2
- data/demo/flags/local_flags.rb +25 -0
- data/demo/flags/remote_flags.rb +18 -0
- data/lib/mixpanel-ruby/events.rb +2 -2
- data/lib/mixpanel-ruby/flags/flags_provider.rb +111 -0
- data/lib/mixpanel-ruby/flags/local_flags_provider.rb +303 -0
- data/lib/mixpanel-ruby/flags/remote_flags_provider.rb +134 -0
- data/lib/mixpanel-ruby/flags/types.rb +35 -0
- data/lib/mixpanel-ruby/flags/utils.rb +65 -0
- data/lib/mixpanel-ruby/groups.rb +1 -1
- data/lib/mixpanel-ruby/people.rb +1 -1
- data/lib/mixpanel-ruby/tracker.rb +32 -2
- data/lib/mixpanel-ruby/version.rb +1 -1
- data/lib/mixpanel-ruby.rb +5 -0
- data/mixpanel-ruby.gemspec +10 -3
- data/spec/mixpanel-ruby/events_spec.rb +2 -2
- data/spec/mixpanel-ruby/flags/local_flags_spec.rb +759 -0
- data/spec/mixpanel-ruby/flags/remote_flags_spec.rb +441 -0
- data/spec/mixpanel-ruby/flags/utils_spec.rb +110 -0
- data/spec/mixpanel-ruby/groups_spec.rb +10 -10
- data/spec/mixpanel-ruby/tracker_spec.rb +5 -5
- data/spec/spec_helper.rb +14 -0
- metadata +117 -9
- data/.travis.yml +0 -8
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Mixpanel
|
|
2
|
+
module Flags
|
|
3
|
+
# Selected variant returned from flag evaluation
|
|
4
|
+
class SelectedVariant
|
|
5
|
+
attr_accessor :variant_key, :variant_value, :experiment_id,
|
|
6
|
+
:is_experiment_active, :is_qa_tester
|
|
7
|
+
|
|
8
|
+
# @param variant_key [String, nil] The variant key
|
|
9
|
+
# @param variant_value [Object] The variant value (any type)
|
|
10
|
+
# @param experiment_id [String, nil] Associated experiment ID
|
|
11
|
+
# @param is_experiment_active [Boolean, nil] Whether experiment is active
|
|
12
|
+
# @param is_qa_tester [Boolean, nil] Whether user is a QA tester
|
|
13
|
+
def initialize(variant_key: nil, variant_value: nil, experiment_id: nil,
|
|
14
|
+
is_experiment_active: nil, is_qa_tester: nil)
|
|
15
|
+
@variant_key = variant_key
|
|
16
|
+
@variant_value = variant_value
|
|
17
|
+
@experiment_id = experiment_id
|
|
18
|
+
@is_experiment_active = is_experiment_active
|
|
19
|
+
@is_qa_tester = is_qa_tester
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Convert to hash representation
|
|
23
|
+
# @return [Hash]
|
|
24
|
+
def to_h
|
|
25
|
+
{
|
|
26
|
+
variant_key: @variant_key,
|
|
27
|
+
variant_value: @variant_value,
|
|
28
|
+
experiment_id: @experiment_id,
|
|
29
|
+
is_experiment_active: @is_experiment_active,
|
|
30
|
+
is_qa_tester: @is_qa_tester
|
|
31
|
+
}.compact
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module Mixpanel
|
|
4
|
+
module Flags
|
|
5
|
+
module Utils
|
|
6
|
+
EXPOSURE_EVENT = '$experiment_started'.freeze
|
|
7
|
+
|
|
8
|
+
# FNV-1a 64-bit hash implementation
|
|
9
|
+
# Used for consistent variant assignment
|
|
10
|
+
#
|
|
11
|
+
# @param data [String] Data to hash
|
|
12
|
+
# @return [Integer] 64-bit hash value
|
|
13
|
+
def self.fnv1a_64(data)
|
|
14
|
+
fnv_prime = 0x100000001b3
|
|
15
|
+
hash_value = 0xcbf29ce484222325
|
|
16
|
+
|
|
17
|
+
data.bytes.each do |byte|
|
|
18
|
+
hash_value ^= byte
|
|
19
|
+
hash_value *= fnv_prime
|
|
20
|
+
hash_value &= 0xffffffffffffffff # Keep 64-bit
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
hash_value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Normalized hash for variant assignment
|
|
27
|
+
# Returns a float in the range [0.0, 1.0) for rollout percentage matching
|
|
28
|
+
#
|
|
29
|
+
# @param key [String] Key to hash (typically distinct_id)
|
|
30
|
+
# @param salt [String] Salt value (flag-specific)
|
|
31
|
+
# @return [Float] Value between 0.0 and 1.0 (non-inclusive upper bound)
|
|
32
|
+
def self.normalized_hash(key, salt)
|
|
33
|
+
combined = key.to_s + salt.to_s
|
|
34
|
+
hash_value = fnv1a_64(combined)
|
|
35
|
+
(hash_value % 100) / 100.0
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Prepare common query parameters for flags API
|
|
39
|
+
#
|
|
40
|
+
# @param token [String] Mixpanel project token
|
|
41
|
+
# @param $lib_version [String] SDK version
|
|
42
|
+
# @return [Hash] Query parameters
|
|
43
|
+
def self.prepare_common_query_params(token, lib_version)
|
|
44
|
+
{
|
|
45
|
+
'mp_lib' => 'ruby',
|
|
46
|
+
'$lib_version' => lib_version,
|
|
47
|
+
'token' => token
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Generate W3C traceparent header for distributed tracing
|
|
52
|
+
# Format: 00-{trace-id}-{parent-id}-{trace-flags}
|
|
53
|
+
#
|
|
54
|
+
# @return [String] traceparent header value
|
|
55
|
+
def self.generate_traceparent
|
|
56
|
+
version = '00'
|
|
57
|
+
trace_id = SecureRandom.hex(16)
|
|
58
|
+
parent_id = SecureRandom.hex(8)
|
|
59
|
+
trace_flags = '01' # sampled
|
|
60
|
+
|
|
61
|
+
"#{version}-#{trace_id}-#{parent_id}-#{trace_flags}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/mixpanel-ruby/groups.rb
CHANGED
data/lib/mixpanel-ruby/people.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require 'mixpanel-ruby/events.rb'
|
|
2
2
|
require 'mixpanel-ruby/people.rb'
|
|
3
3
|
require 'mixpanel-ruby/groups.rb'
|
|
4
|
+
require 'mixpanel-ruby/flags/local_flags_provider.rb'
|
|
5
|
+
require 'mixpanel-ruby/flags/remote_flags_provider.rb'
|
|
4
6
|
|
|
5
7
|
module Mixpanel
|
|
6
8
|
# Use Mixpanel::Tracker to track events and profile updates in your application.
|
|
@@ -33,6 +35,14 @@ module Mixpanel
|
|
|
33
35
|
# An instance of Mixpanel::Groups. Use this to send groups updates
|
|
34
36
|
attr_reader :groups
|
|
35
37
|
|
|
38
|
+
# An instance of Mixpanel::Flags::LocalFlagsProvider. Use this for
|
|
39
|
+
# client-side feature flag evaluation
|
|
40
|
+
attr_reader :local_flags
|
|
41
|
+
|
|
42
|
+
# An instance of Mixpanel::Flags::RemoteFlagsProvider. Use this for
|
|
43
|
+
# server-side feature flag evaluation
|
|
44
|
+
attr_reader :remote_flags
|
|
45
|
+
|
|
36
46
|
# Takes your Mixpanel project token, as a string.
|
|
37
47
|
#
|
|
38
48
|
# tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
|
|
@@ -52,11 +62,31 @@ module Mixpanel
|
|
|
52
62
|
# If a block is provided, it is passed a type (one of :event or :profile_update)
|
|
53
63
|
# and a string message. This same format is accepted by Mixpanel::Consumer#send!
|
|
54
64
|
# and Mixpanel::BufferedConsumer#send!
|
|
55
|
-
def initialize(token, error_handler=nil, &block)
|
|
65
|
+
def initialize(token, error_handler=nil, local_flags_config: nil, remote_flags_config: nil, &block)
|
|
56
66
|
super(token, error_handler, &block)
|
|
57
67
|
@token = token
|
|
58
68
|
@people = People.new(token, error_handler, &block)
|
|
59
69
|
@groups = Groups.new(token, error_handler, &block)
|
|
70
|
+
|
|
71
|
+
# Initialize local flags if config provided
|
|
72
|
+
if local_flags_config
|
|
73
|
+
@local_flags = Flags::LocalFlagsProvider.new(
|
|
74
|
+
token,
|
|
75
|
+
local_flags_config,
|
|
76
|
+
method(:track), # Pass bound method as callback
|
|
77
|
+
error_handler || ErrorHandler.new
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Initialize remote flags if config provided
|
|
82
|
+
if remote_flags_config
|
|
83
|
+
@remote_flags = Flags::RemoteFlagsProvider.new(
|
|
84
|
+
token,
|
|
85
|
+
remote_flags_config,
|
|
86
|
+
method(:track), # Pass bound method as callback
|
|
87
|
+
error_handler || ErrorHandler.new
|
|
88
|
+
)
|
|
89
|
+
end
|
|
60
90
|
end
|
|
61
91
|
|
|
62
92
|
# A call to #track is a report that an event has occurred. #track
|
|
@@ -165,7 +195,7 @@ module Mixpanel
|
|
|
165
195
|
properties = {
|
|
166
196
|
'distinct_id' => distinct_id,
|
|
167
197
|
'token' => @token,
|
|
168
|
-
'time' => Time.now.to_f,
|
|
198
|
+
'time' => (Time.now.to_f * 1000).to_i,
|
|
169
199
|
'mp_lib' => 'ruby',
|
|
170
200
|
'$lib_version' => Mixpanel::VERSION,
|
|
171
201
|
}.merge(properties)
|
data/lib/mixpanel-ruby.rb
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
require 'mixpanel-ruby/consumer.rb'
|
|
2
2
|
require 'mixpanel-ruby/tracker.rb'
|
|
3
3
|
require 'mixpanel-ruby/version.rb'
|
|
4
|
+
require 'mixpanel-ruby/flags/utils.rb'
|
|
5
|
+
require 'mixpanel-ruby/flags/types.rb'
|
|
6
|
+
require 'mixpanel-ruby/flags/flags_provider.rb'
|
|
7
|
+
require 'mixpanel-ruby/flags/local_flags_provider.rb'
|
|
8
|
+
require 'mixpanel-ruby/flags/remote_flags_provider.rb'
|
data/mixpanel-ruby.gemspec
CHANGED
|
@@ -12,10 +12,17 @@ spec = Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.homepage = 'https://mixpanel.com/help/reference/ruby'
|
|
13
13
|
spec.license = 'Apache License 2.0'
|
|
14
14
|
|
|
15
|
-
spec.required_ruby_version = '>=
|
|
15
|
+
spec.required_ruby_version = '>= 3.0.0'
|
|
16
|
+
spec.add_runtime_dependency 'mutex_m'
|
|
17
|
+
spec.add_runtime_dependency "base64"
|
|
18
|
+
spec.add_runtime_dependency 'json-logic-rb', '~> 0.1.5'
|
|
16
19
|
|
|
17
20
|
spec.add_development_dependency 'activesupport', '~> 4.0'
|
|
18
|
-
spec.add_development_dependency 'rake', '~>
|
|
21
|
+
spec.add_development_dependency 'rake', '~> 13'
|
|
19
22
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
20
|
-
spec.add_development_dependency 'webmock', '~>
|
|
23
|
+
spec.add_development_dependency 'webmock', '~> 3.16.2'
|
|
24
|
+
spec.add_development_dependency 'debug'
|
|
25
|
+
spec.add_development_dependency 'ruby-lsp-rspec'
|
|
26
|
+
spec.add_development_dependency 'simplecov'
|
|
27
|
+
spec.add_development_dependency 'simplecov-cobertura'
|
|
21
28
|
end
|
|
@@ -27,7 +27,7 @@ describe Mixpanel::Events do
|
|
|
27
27
|
'mp_lib' => 'ruby',
|
|
28
28
|
'$lib_version' => Mixpanel::VERSION,
|
|
29
29
|
'token' => 'TEST TOKEN',
|
|
30
|
-
'time' => @time_now.to_i
|
|
30
|
+
'time' => @time_now.to_i * 1000
|
|
31
31
|
}
|
|
32
32
|
}]])
|
|
33
33
|
end
|
|
@@ -46,7 +46,7 @@ describe Mixpanel::Events do
|
|
|
46
46
|
'mp_lib' => 'ruby',
|
|
47
47
|
'$lib_version' => Mixpanel::VERSION,
|
|
48
48
|
'token' => 'TEST TOKEN',
|
|
49
|
-
'time' => @time_now.to_i
|
|
49
|
+
'time' => @time_now.to_i * 1000
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
} ]])
|