easy_ab 0.3.0 → 0.4.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 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