Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init version #3

Merged
merged 44 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
13efceb
init
chylli-deriv Jun 21, 2024
65f68e9
run test on pr
chylli-deriv Jun 21, 2024
e8d3f66
add cases.json
chylli-deriv Jun 23, 2024
55d61ea
init
chylli-deriv Jun 24, 2024
6bbc726
init
chylli-deriv Jun 25, 2024
8154291
feature repository
chylli-deriv Jun 25, 2024
404734d
fix header
chylli-deriv Jun 25, 2024
93830fd
feature module
chylli-deriv Jun 25, 2024
e585dd1
on off, and featureresult
chylli-deriv Jun 25, 2024
e24b916
eval feature
chylli-deriv Jun 25, 2024
84b4d70
load_feature_value
chylli-deriv Jun 25, 2024
1e30b60
fix problems
chylli-deriv Jun 25, 2024
0165266
fix problems
chylli-deriv Jun 25, 2024
1f0ceee
remove dumper
chylli-deriv Jun 25, 2024
7827cb7
feature_result test
chylli-deriv Jun 26, 2024
9e5afbd
repository test ok
chylli-deriv Jun 26, 2024
d48606f
growthbook test ok
chylli-deriv Jun 26, 2024
3c1cceb
set cpanfile
chylli-deriv Jun 26, 2024
7944174
rename build file
chylli-deriv Jun 26, 2024
a01cbab
try fix version
chylli-deriv Jun 26, 2024
ec6307a
add pod
chylli-deriv Jun 26, 2024
bd6be94
changes
chylli-deriv Jun 26, 2024
e469e45
typo
chylli-deriv Jun 26, 2024
de285e7
fix eol
chylli-deriv Jun 26, 2024
53a7f54
move test data to __DATA__
chylli-deriv Jun 26, 2024
def6230
format json
chylli-deriv Jun 26, 2024
ae7551c
split test data to a json file
chylli-deriv Jun 26, 2024
301c6f9
update cpanfile
chylli-deriv Jun 26, 2024
457db85
typo
chylli-deriv Jun 26, 2024
504a290
return undef when a feature not exist
chylli-deriv Jun 26, 2024
f3bab1e
add cache
chylli-deriv Jun 27, 2024
bf3e851
singleton cache
chylli-deriv Jun 27, 2024
585edff
test cache
chylli-deriv Jun 27, 2024
2208490
update README.md
chylli-deriv Jun 28, 2024
3c14b56
Update examples/feature.pl
chylli-deriv Jun 28, 2024
beae143
make client_key required
chylli-deriv Jun 28, 2024
20814b8
return false when not load features.
chylli-deriv Jul 1, 2024
fd812bf
error log if call on/off on non-boolean
chylli-deriv Jul 1, 2024
ba86173
md5 key
chylli-deriv Jul 2, 2024
d1fea96
add a debug info
chylli-deriv Jul 2, 2024
e1a8dd4
add VERSION
chylli-deriv Jul 2, 2024
05ce62e
add md5
chylli-deriv Jul 2, 2024
c2b5c59
fix Object::Pad syntax error under 0.805
chylli-deriv Jul 2, 2024
42aff1a
Release WebService-GrowthBook 0.001
chylli-deriv Jul 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/build-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: build and test
run-name: build and test
on:
push:
branches:
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
perl-version:
- '5.38'
- '5.36'
- '5.34'
- '5.32'
- '5.30'

container:
image: perldocker/perl-tester:${{ matrix.perl-version }}

steps:
- uses: actions/checkout@main
- run: perl -V
- run: |
cpm install -g --no-test Dist::Zilla Dist::Zilla::App::Command::cover ExtUtils::MakeMaker
name: Install Dzil
- name: Install dzil author dependencies
run: |
cpm install --no-test -g \
-w 2 \
--mirror=http://cpan.cpantesters.org/ $(dzil authordeps --missing)
- name: Install dist deps
run: |
cpanm -n --installdeps .
dzil listdeps --author --missing --cpanm-versions | xargs cpanm -n

- run: dzil smoke --release --author && dzil cover -test -report codecov && dzil xtest
chylli-deriv marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 0 additions & 20 deletions .github/workflows/github-actions-demo.yml

This file was deleted.

24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
docker-compose.yml

id_dsa
id_rsa
/blib/
/.build/
_build/
cover_db/
inc/
Build
!Build/
Build.bat
.last_cover_stats
/Makefile
/Makefile.old
/MANIFEST.bak
/META.yml
/META.json
/MYMETA.*
nytprof.out
/pm_to_blib
*.o
*.bs
*.swp
7 changes: 7 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{$NEXT}}
- Initial release
- load features
- get feature value
- is_on
- is_off

73 changes: 72 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,72 @@
# WebService-GrowthBook
# NAME

WebService::GrowthBook - sdk of growthbook

# SYNOPSIS

use WebService::GrowthBook;
my $instance = WebService::GrowthBook->new(client_key => 'my key');
$instance->load_features;
if($instance->is_on('feature_name')){
# do something
}
else {
# do something else
}
my $string_feature = $instance->get_feature_value('string_feature');
my $number_feature = $instance->get_feature_value('number_feature');
# get decoded json
my $json_feature = $instance->get_feature_value('json_feature');

# DESCRIPTION

This module is a sdk of growthbook, it provides a simple way to use growthbook features.

# METHODS

## load\_features

load features from growthbook API

$instance->load_features;

## is\_on

check if a feature is on

$instance->is_on('feature_name');

Please note it will return undef if the feature does not exist.

## is\_off

check if a feature is off

$instance->is_off('feature_name');

Please note it will return undef if the feature does not exist.

## get\_feature\_value

get the value of a feature

$instance->get_feature_value('feature_name');

Please note it will return undef if the feature does not exist.

## set\_features

set features

$instance->set_features($features);

## eval\_feature

evaluate a feature to get the value

$instance->eval_feature('feature_name');

# SEE ALSO

- [https://docs.growthbook.io/](https://docs.growthbook.io/)
- [PYTHON VERSION](https://github.com/growthbook/growthbook-python)
21 changes: 21 additions & 0 deletions cpanfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
requires 'indirect', '>= 0.37';
requires 'Object::Pad', '>= 0.805';
requires 'JSON::MaybeUTF8';
requires 'Scalar::Util';
requires 'HTTP::Tiny';
requires 'Log::Any';
requires 'Syntax::Keyword::Try';


on test => sub {
requires 'Test::More', '>= 0.98';
requires 'Test::Exception';
requires 'Test::MockModule';
requires 'Path::Tiny';
requires 'FindBin';
};

on develop => sub {
requires 'Devel::Cover', '>= 1.23';
requires 'Devel::Cover::Report::Codecov', '>= 0.14';
};
12 changes: 12 additions & 0 deletions dist.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name = WebService-GrowthBook
author = DERIV <[email protected]>
license = Perl_5
copyright_holder = Deriv Services Ltd
copyright_year = 2022

[@Author::DERIV]
allow_dirty = lib/WebService/GrowthBook.pm
[PerlTidy]
perltidyrc = t/rc/perltidyrc
[Test::Perl::Critic]
critic_config = t/rc/perlcriticrc
14 changes: 14 additions & 0 deletions examples/feature.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use strict;
use warnings;
use WebService::GrowthBook;
use WebService::GrowthBook::FeatureRepository;
use Data::Dumper;
use Log::Any::Adapter qw(Stdout);

my $key = $ENV{GROWTHBOOK_KEY};
my $gb = WebService::GrowthBook->new(client_key => $key);
$gb->load_features();
print "bool feature is on:", $gb->is_on('bool-feature'), "\n";
print "string feature:", $gb->get_feature_value('string-feature'),"\n";
print "number feature:", $gb->get_feature_value('number-feature'),"\n";
print "json feature:", Dumper($gb->get_feature_value('json-feature')),"\n";
chylli-deriv marked this conversation as resolved.
Show resolved Hide resolved
chylli-deriv marked this conversation as resolved.
Show resolved Hide resolved
174 changes: 174 additions & 0 deletions lib/WebService/GrowthBook.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package WebService::GrowthBook;
# ABSTRACT: ...

use strict;
use warnings;
no indirect;
use feature qw(state);
use Object::Pad;
use JSON::MaybeUTF8 qw(decode_json_text);
use Scalar::Util qw(blessed);
use Log::Any qw($log);
use WebService::GrowthBook::FeatureRepository;
use WebService::GrowthBook::Feature;
use WebService::GrowthBook::FeatureResult;
use WebService::GrowthBook::InMemoryFeatureCache;

our $VERSION = '0.001';

=head1 NAME

WebService::GrowthBook - sdk of growthbook

=head1 SYNOPSIS

use WebService::GrowthBook;
my $instance = WebService::GrowthBook->new(client_key => 'my key');
$instance->load_features;
if($instance->is_on('feature_name')){
# do something
}
else {
# do something else
}
my $string_feature = $instance->get_feature_value('string_feature');
my $number_feature = $instance->get_feature_value('number_feature');
# get decoded json
my $json_feature = $instance->get_feature_value('json_feature');

=head1 DESCRIPTION

This module is a sdk of growthbook, it provides a simple way to use growthbook features.

=cut

# singletons

class WebService::GrowthBook {
field $url :param //= 'https://api.growthbook.io/api/v1';
field $client_key :param //= '';
field $features :param //= {};
field $cache_ttl :param //= 60;
field $cache //= WebService::GrowthBook::InMemoryFeatureCache->singleton;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this cache object here ? From my understanding The FeatureRepository seems to be abstracting the cache from the the Growthbook class Object ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter is used to set cache module in FeatureRepository. Without this one we cannot custom cache object in FeatureRepository.

method load_features {
if(!$client_key) {
die "Must specify 'client_key' to refresh features";
}
chylli-deriv marked this conversation as resolved.
Show resolved Hide resolved
my $feature_repository = WebService::GrowthBook::FeatureRepository->new(cache => $cache);
my $loaded_features = $feature_repository->load_features($url, $client_key, $cache_ttl);
if($loaded_features){
$self->set_features($loaded_features);
}
return 1;
chylli-deriv marked this conversation as resolved.
Show resolved Hide resolved
}
method set_features($features_set) {
$features = {};
for my $feature ($features_set->@*) {
if(blessed($feature) && $feature->isa('WebService::GrowthBook::Feature')){
$features->{$feature->id} = $feature;
}
else {
$features->{$feature->{id}} = WebService::GrowthBook::Feature->new(id => $feature->{id}, default_value => $feature->{defaultValue}, value_type => $feature->{valueType});
}
}
}

method is_on($feature_name) {
my $result = $self->eval_feature($feature_name);
return undef unless defined($result);
return $result->on;
}

method is_off($feature_name) {
my $result = $self->eval_feature($feature_name);
return undef unless defined($result);
return $result->off;
}
chylli-deriv marked this conversation as resolved.
Show resolved Hide resolved

method eval_feature($feature_name){
if(!exists($features->{$feature_name})){
$log->errorf("No such feature: %s", $feature_name);
return undef;
}
my $feature = $features->{$feature_name};
my $default_value = $feature->default_value;
if($feature->value_type eq 'json'){
$default_value = decode_json_text($default_value);
}
elsif($feature->value_type eq 'number'){
$default_value = 0 + $default_value;
}
elsif($feature->value_type eq 'boolean'){
$default_value = $default_value eq 'true' ? 1 : 0;
}

return WebService::GrowthBook::FeatureResult->new(
value => $default_value);
}

method get_feature_value($feature_name){
my $result = $self->eval_feature($feature_name);
return undef unless defined($result);
return $result->value;
}
}

=head1 METHODS

=head2 load_features

load features from growthbook API

$instance->load_features;

=head2 is_on

check if a feature is on

$instance->is_on('feature_name');

Please note it will return undef if the feature does not exist.

=head2 is_off

check if a feature is off

$instance->is_off('feature_name');

Please note it will return undef if the feature does not exist.

=head2 get_feature_value

get the value of a feature

$instance->get_feature_value('feature_name');

Please note it will return undef if the feature does not exist.

=head2 set_features

set features

$instance->set_features($features);

=head2 eval_feature

evaluate a feature to get the value

$instance->eval_feature('feature_name');

=cut

1;


=head1 SEE ALSO

=over 4

=item * L<https://docs.growthbook.io/>

=item * L<PYTHON VERSION|https://github.com/growthbook/growthbook-python>

=back

Loading
Loading