· 5 years ago · Jun 15, 2020, 05:18 PM
1# You were presented with a following request from mobile team:
2#
3# - We want to show in-app banners to our user
4# - We want to be able to create/delete/deactivate banners from site admin
5# - We want to specify application screen and user cohort for a banner
6# Possible screens: [ deliveries, meal_selection ]
7# Possible user cohorts: [ trial, loyal, returning, all ]
8# - We want to be able to specify A/B test key for a banner, and only show it to users participating as variant in this experiment
9# - At a given screen user should only be eligible for one banner
10# - We want an API endpoint to fetch a banner for the user
11# - We want information about banners for user to be included in "/api/v1/initial_data" API endpoint(an endpoint that gathers all data for an initial app load and is thoroughly cached)
12# - Further down the road we will want other things(like footer, etc.) implemented with the same rules(screen, cohort matching)
13#
14# Let's implement given request on a high-level(class structure, db tables)
15
16
17
18User. group
19Banner
20
21
22Service that returns need benner by screen (token user id and group) -> banner -> A\B test API -> banner or nil
23
24class Banner < Model
25 title
26 decr
27 id
28 experiment_key
29
30 screen :enum, [:delivery, :prices]
31end
32
33
34class User < Model
35 id
36 token
37
38 def group
39 end
40end
41
42-------------
43
44class API::BannersController < ApplicationController
45 def show_banner
46 result = ::Services::Banners::FindBanner.new().call(current_user, banner_params[:screen])
47
48 if result.success?
49 banner = result.value
50
51 return json: { status: :ok, BannerSerializer.new(banner) }
52 end
53 end
54
55 private
56
57 def banner_params
58 params.require(:banner).permit(:screen)
59 end
60end
61
62------------
63
64class Services::Banners::FindBanner < ::Base::Service
65 attr_reader :user
66 attr_reader :screen
67
68 def call
69 cohorta = user.cohorta
70
71 banner = Banner.find_by(user: user, screen: screen)
72
73 return success unless banner
74 return success(banner) unless banner.experiment_key
75
76 experiment_user_group = Expirement.find_by(key: banner.experiment_key)&.assign(user) #=> #{user_group}
77
78 experiment_user_group ? success(banner) : success
79 end
80end
81
82-------------------------
83
84
85context 'valid user & screen' do
86 it 'successfully returns a banner when there is an experiment'
87 end
88
89 it 'successfully returns a banner when there is no an experiment'
90 end
91
92 it 'returns success without banner when there is no banner'
93 end
94end
95
96context 'invalid user & screen' do
97 it 'returns success without banner when there is no banner with such screen'
98 end
99end
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114----
115
116def succees(value)
117 [true, value]
118end
119
120def error
121 [false, nil]
122end