easy_ab 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a56efe571167899ce6a67e013c3815c3ac9bd8b
4
- data.tar.gz: 8a3b0357ce34f6e420e7aa10276afc3a09eb1266
3
+ metadata.gz: 068d106a909b0229bc7fc28960f275fca34bd5e9
4
+ data.tar.gz: 411b8a8f520e4d4f1dd627065e1105b08175acee
5
5
  SHA512:
6
- metadata.gz: 1315a89fd9ad3cb5e1fede3ab862c6608222cbe5faba02b2604a8eff3cd3b66c875a90d95ca4a89db687f56a3fb408dedbed7ee8b47c66f5915901171b02536b
7
- data.tar.gz: 5e40c5b8f23e94a782e2a9b9dcb9f66711384be59b734bff18281e5adb1eeb25ca85db7dd0465d7eb36942dbb401e02be3c23eebfe18d519b0546afbca8f9ccf
6
+ metadata.gz: 4429ad954dcf221b315d43766c04446e72ee1bb21cef672510e1aebd17214d9224b0a20fa0af06755f52e8818c6da1f1cca317827085fe8c6b03ee956701cb8f
7
+ data.tar.gz: 01924f52c1e3a40dde96fcfee2bf8e5d39b79b2f16df5b8c55a4a51e71e471ae60984a2452a06a8e6b51d03654c6d1e3ee539db45a141219b91a742e0e600916
data/CHANGELOG.md CHANGED
@@ -1,14 +1,17 @@
1
- # HEAD
2
- * Supports scope in config to define whether a user can join an experiment or not.
1
+ # 0.4.0 (2017-08-16)
2
+ - You can specify user in `ab_test()` This useful when requests do not contain current_user. A well-known example is controllers which handle payment results by listening requests from 3rd party payment gateway.
3
+
4
+ # 0.3.0 (2017-08-16)
5
+ - Supports scope in config to define whether a user can join an experiment or not.
3
6
 
4
7
  # 0.2.0 (2017-08-15)
5
- * **API change**: If all rules failed, `ab_test` returns nil, instead of the first variant.
8
+ - **API change**: If all rules failed, `ab_test` returns nil, instead of the first variant.
6
9
 
7
10
  # 0.1.0
8
- * Support winner
11
+ - Support winner
9
12
 
10
13
  # 0.0.3
11
- * Add new API `all_participated_experiments` to list current user's all participated experiments
14
+ - Add new API `all_participated_experiments` to list current user's all participated experiments
12
15
 
13
16
  # 0.0.1
14
- * The first version :)
17
+ - The first version :)
@@ -5,7 +5,7 @@ module EasyAb
5
5
  validates :experiment, presence: true
6
6
  validates :variant, presence: true
7
7
  validates :user_id, uniqueness: { scope: [:experiment] }
8
- validates :cookie, uniqueness: { scope: [:experiment] }
8
+ validate :cookie_should_be_unique_when_user_id_is_nil
9
9
  validate :user_should_be_present
10
10
 
11
11
  private
@@ -16,5 +16,11 @@ module EasyAb
16
16
  errors.add(:cookie, "or user_id can't be blank")
17
17
  end
18
18
  end
19
+
20
+ def cookie_should_be_unique_when_user_id_is_nil
21
+ if user_id.nil?
22
+ errors.add(:cookie, "already exists") if self.class.where(cookie: cookie, user_id: nil).exists?
23
+ end
24
+ end
19
25
  end
20
26
  end
@@ -64,29 +64,35 @@ module EasyAb
64
64
  grouping.variant
65
65
  end
66
66
 
67
+ # TODO: add spec
67
68
  def find_grouping_by_user_recognition(user_recognition)
68
- user_id = user_recognition[:id]
69
- cookie = user_recognition[:cookie]
69
+ user_id = user_recognition[:id].presence
70
+ cookie = user_recognition[:cookie].presence
71
+ raise 'User not found: both user_id and cookie are empty' if user_id.nil? && cookie.nil?
72
+
73
+ # Cases should take into consideration
74
+ # Case I: user participated experiment with login and return again
75
+ # Case II: user participated experiment with login and return by another device with login
76
+ # Case III: user participated experiment with login and return by the same device, but cookie was cleared between last and this participation
77
+ # => Both II and III already exist a record with the same user_id but different cookie
78
+ # In the above two cases, we update the cookie of the exising record
79
+ #
80
+ # Case IV: User participated experiment without login and return with login
81
+ # => Assign user_id to the existing record
82
+ #
83
+ # Case V: User participated experiment without login and return without login, too
70
84
  grouping = nil
71
-
72
- raise 'should assign a cookie' unless cookie
73
-
74
- if user_id # If user login
75
- # Case I: User participated experiment with login and return again
76
- # Case II: user participated experiment with login and return by another device with login
77
- # Case III: user participated experiment with login and return by the same device, but cookie was cleared between last and this participation
78
- # => Both II and III already exist a record with the same user_id but different cookie
79
- # In the above two cases, we update the cookie of the exising record
85
+ if user_id # User is signed in
86
+ # Case I, II, III
80
87
  return grouping if (grouping = groupings.where(user_id: user_id).first) && ((cookie && grouping.cookie = cookie) || true)
81
-
82
- # User participated experiment without login, but this time with login => assign user_id to the existing record
83
- return grouping if (grouping = groupings.where(user_id: nil, cookie: cookie).first) && grouping.user_id = user_id
84
- else # If user not login
88
+ # Case IV
89
+ return grouping if (cookie && grouping = groupings.where(user_id: nil, cookie: cookie).first) && ((grouping.user_id = user_id) || true) # Assign user_id
90
+ elsif cookie # User is not signed in
91
+ # Case V
85
92
  return grouping if grouping = groupings.where(cookie: cookie).first
86
93
  end
87
94
 
88
- # User have yet to participate experiment
89
- nil
95
+ grouping
90
96
  end
91
97
 
92
98
  def groupings
@@ -7,7 +7,7 @@ module EasyAb
7
7
 
8
8
  if respond_to?(:request) && params[:ab_test] && params[:ab_test][experiment_name]
9
9
  # Check current user is admin or not by proc defined by gem user
10
- if current_user_is_admin?
10
+ if easy_ab_user_is_admin?(options)
11
11
  options[:variant] ||= params[:ab_test][experiment_name]
12
12
  end
13
13
  # TODO: exclude bot
@@ -15,21 +15,23 @@ module EasyAb
15
15
 
16
16
  experiment = EasyAb::Experiment.find_by_name!(experiment_name)
17
17
 
18
- # Obtain context
18
+ # Obtain context for rules
19
19
  if experiment.rules.present?
20
20
  @rules_with_current_context ||= experiment.rules.map { |rule| Proc.new { instance_exec(&rule)} }
21
21
  options[:contexted_rules] = @rules_with_current_context
22
22
  end
23
23
 
24
- # Obtain context
24
+ # Obtain context for scope
25
25
  if experiment.scope.present?
26
26
  @scope ||= Proc.new { instance_exec(&experiment.scope) }
27
27
  options[:scope] = @scope
28
28
  end
29
29
 
30
30
  @variant_cache ||= {}
31
- @variant_cache[experiment_name] ||= experiment.assign_variant(user_recognition, options)
32
- block_given? ? yield(@variant_cache[experiment_name]) : @variant_cache[experiment_name]
31
+ @variant_cache[easy_ab_user_id(options)] ||= {}
32
+ @variant_cache[easy_ab_user_id(options)][experiment_name] ||= experiment.assign_variant(user_recognition, options)
33
+ variant = @variant_cache[easy_ab_user_id(options)][experiment_name]
34
+ block_given? ? yield(variant) : variant
33
35
  end
34
36
 
35
37
  # Return all participated experiments and the corresponding variants for current user
@@ -62,21 +64,46 @@ module EasyAb
62
64
  # TODO:
63
65
  # return (raise NotImplementedError) if options[:user] && (users << options[:user])
64
66
 
65
- user_recognition[:id] = current_user_id if current_user_signed_in?
67
+ # if options[:user] && options[:user].id
68
+ # user_recognition[:id] = options[:user].id
69
+ # else
70
+ # user_recognition[:id] = current_user_id if current_user_signed_in?
71
+ # end
72
+
73
+ user_recognition[:id] = easy_ab_user_id(options)
74
+
66
75
  # Controllers and views
67
76
  user_recognition[:cookie] = find_or_create_easy_ab_cookie if respond_to?(:request)
68
77
 
69
78
  user_recognition
70
79
  end
71
80
 
81
+ def easy_ab_user_signed_in?
82
+ current_user_signed_in?
83
+ end
84
+
72
85
  def current_user_signed_in?
73
86
  user_signed_in_method_proc.call
74
87
  end
75
88
 
89
+ def easy_ab_user_id(options)
90
+ if options[:user]
91
+ options[:user].id
92
+ elsif easy_ab_user_signed_in?
93
+ current_user_id
94
+ else
95
+ nil
96
+ end
97
+ end
98
+
76
99
  def current_user_id
77
100
  current_user_id_proc.call
78
101
  end
79
102
 
103
+ def easy_ab_user_is_admin?(options)
104
+ options[:user] ? false : current_user_is_admin?
105
+ end
106
+
80
107
  def current_user_is_admin?
81
108
  authorize_admin_with_proc.call
82
109
  end
@@ -1,3 +1,3 @@
1
1
  module EasyAb
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_ab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gary Chu