Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Bod

by Bod (Vicar)
on Nov 15, 2020 at 00:48 UTC ( #11123653=user: print w/replies, xml ) Need Help??

Long time amateur coder since growing up with a ZX Spectrum and BBC Micro...

Introduced to Perl in the early 1990's which quickly became the language of choice. Built many websites and backend applications using Perl including the sites for my property business:
Lets Delight - company site
Lets Stay - booking site
Also a few simple TK based desktop apps to speed things up.

Guilty of only learning what I need to get the job done - a recipe for propagating bad practice and difficult to maintain code...difficult for me so good luck to anyone else!

Now (Nov 2020) decided to improve my coding skills although I'm not really sure what "improve" means in this context. It seems Perl and best practice have come along way since I last checked in and my programming is approach is stuck in the last decade.

Onwards and upwards...

20th October 2021 - added to Saint in our Book 😀
2nd October 2022 - promoted to Priest
7th July 2023 - promoted to Vicar


Find me on LinkedIn


CPAN Release

Business::Stripe::WebCheckout


Posts by Bod
Multidimensional arrays in Seekers of Perl Wisdom
5 direct replies — Read more / Contribute
by Bod
on Jun 06, 2023 at 10:55

    We operate a few sites with subscriptions. Until recently, the subscription cost has been fixed with just three plan options - monthly, 6 monthly or annually. However, we now want to increase the cost on one site for new members but not existing members. Existing members will have their current subscription rate locked for life.

    Currently, the three subscription rates are declared in a parameters file:

    our $stripe_price_1 = 'price_123245'; our $stripe_price_6 = 'price_123267'; our $stripe_price_12 = 'price_123289';
    The price code is passed to Stripe during the checkout process.

    Having multiple prices in the future means we need to adopt a different approach. Users have the option to change between payment plans so they can swap from monthly or annually, etc. Therefore, we need to know which pricing level they are on as well as the plan. We can (and currently do) easily get the plan from Stripe. But getting the level is more tricky. We'd have to get the price code and work backwards.

    So, I am thinking of adding two fields to the Subscription table of the database - priceLevel and pricePlan - and replacing the above declarations with a two dimensional Perl array of hashref:

    my @price; $price[1,1] = { 'name' => 'Early Adopter', 'plan' => 1, 'length' => 1, 'price' => 3.49, 'stripe' => 'price_123245', }; $price[1,6] = { 'name' => 'Early Adopter', 'plan' => 1, 'length' => 6, 'price' => 18.99, 'stripe' => 'price_123267', }; $price[1,12] = { 'name' => 'Early Adopter', 'plan' => 1, 'length' => 12, 'price' => 32.49, 'stripe' => 'price_123289', }; $price[2,1] = { 'name' => 'First Increase', 'plan' => 2, 'length' => 1, 'price' => 3.99, 'stripe' => 'price_123445', }; $price[2,6] = { ... }; $price[2,12] = { 'name' => 'First Increase', 'plan' => 2, 'length' => 12, 'price' => 33.99, 'stripe' => 'price_123489', }; $price[3,1] = { ... }; $price[3,6] = { ... }; $price[3,12] = { ... };
    The two dimensions of the array corresponding to the two newly added database fields.

    I have two doubts about this approach...

    1. There are lots of 'spaces' in the array
    2. I seem to recall reading (probably) in The Monastry that multidimensional arrays are not a good idea

    Any advice very welcome...

Please review documentation of my AI::Embedding module in Meditations
2 direct replies — Read more / Contribute
by Bod
on Jun 02, 2023 at 17:26

    Could you please take a look at the documentation for my new module and let me know if it makes sense? I always find that I am too close to the module and know what everything is supposed to do. In short, I have the Curse of Knowledge!

    Here is the documentation

    Why is it you only find typos after publishing?
    The second raw_embedding method should read test_embedding in both the heading and the sample code. I've corrected this error now.

    Thank you greatly for helping me get this right...

    Edit:

    Changed title from "RFC - Documentation Review" to "Please review documentation of my AI::Embedding module" as considered by erzuuli

CPAN Testers in Seekers of Perl Wisdom
3 direct replies — Read more / Contribute
by Bod
on May 30, 2023 at 17:46
Test::More fails... in Seekers of Perl Wisdom
3 direct replies — Read more / Contribute
by Bod
on May 29, 2023 at 19:25

    My experience of tests is very limited so the experience of The Monestry is greatly appreciated

    I am using Test::More and I have two issues.

    Firstly...

    #!perl use 5.006; use strict; use warnings; use Test::More; plan tests => 1; BEGIN { use_ok( 'AI::Embedding' ) || print "Bail out!\n"; } diag( "Testing AI::Embedding $AI::Embedding::VERSION, Perl $], $^X" );
    This fails at plan tests => 1; despite this being added by Module::Starter and it appearing correct according to the documentation. Instead, I have to write use Test::More tests => 1;. What am I doing wrong?

    Second issue...

    my $comp_pass1 = $embed_pass->compare('-0.6,-0.5,-0.4,-0.3,-0.2,0.0,0. +2,0.3,0.4,0.5', '-0.6,-0.5,-0.4,-0.3,-0.2,0.0,0.2,0.3,0.4,0.5'); ok( $comp_pass1 == 1, "Compare got $comp_pass1"); $embed_pass->comparator('-0.6,-0.5,-0.4,-0.3,-0.2,0.0,0.2,0.3,0.4,0.5' +); my $comp_pass2 = $embed_pass->compare('-0.6,-0.5,-0.4,-0.3,-0.2,0.0,0. +2,0.3,0.4,0.5'); ok( $comp_pass2 == 1, "Compare to comparator got $comp_pass2");
    This code is failing despite $comp_pass1 and $comp_pass2 being 1. Again, what am I doing wrong?

    This is the output from gmake test

    "C:\Strawberry\perl\bin\perl.exe" "-MExtUtils::Command::MM" "-MTest::H +arness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib\l +ib', 'blib\arch')" t/*.t t/00-load.t ....... 1/1 # Testing AI::Embedding 0.1_1, Perl 5.032001, +C:\Strawberry\perl\bin\perl.exe t/00-load.t ....... ok t/01-openai.t ..... 1/11 # Failed test 'Compare to comparator got 1' # at t/01-openai.t line 44. # Looks like you failed 1 test of 11. t/01-openai.t ..... Dubious, test returned 1 (wstat 256, 0x100) Failed 1/11 subtests t/manifest.t ...... skipped: Author tests not required for installatio +n t/pod-coverage.t .. skipped: Author tests not required for installatio +n t/pod.t ........... skipped: Author tests not required for installatio +n Test Summary Report ------------------- t/01-openai.t (Wstat: 256 Tests: 11 Failed: 1) Failed test: 11 Non-zero exit status: 1 Files=5, Tests=12, 0 wallclock secs ( 0.05 usr + 0.02 sys = 0.06 CP +U) Result: FAIL Failed 1/5 test programs. 1/12 subtests failed. gmake: *** [Makefile:859: test_dynamic] Error 255

How Critical is Critic? in Seekers of Perl Wisdom
8 direct replies — Read more / Contribute
by Bod
on May 28, 2023 at 20:18

    As the new module is pretty much written I have thrown it at Perl Critic which has detected a couple of issues. But as I have wound up the severity, it seems a little silly...

    How critical is it that code destined for CPAN passes critic, if at all?
    And what severity level would be sensible?

    I was surprised it didn't like this use of eval:

    our $VERSION = '0.1_1'; $VERSION = eval $VERSION;
    and I struggle to see what benefit adding the /x flag would be to such a simple regular expression:
    my @embed = split /,/, $embed_string;
    With a higher severity, it wants me to add lots of flags - /xms - to this!

    The one where I want to change it but can't quite see a better way to write it without losing clarity is this:

    # Create Embedding object sub new { my $class = shift; my %attr = @_; # ...... }
    Critic says I should unpack @_ on the first line but is it really that bad to split it across two consecutive lines? There would be a problem if it was part way through the sub but it is clear what is going on and there is no chance of the modifying the wrong thing through an alias.

    Am I missing some lurking danger here?

CPAN Ratings... in Seekers of Perl Wisdom
3 direct replies — Read more / Contribute
by Bod
on May 28, 2023 at 19:41

    I've had a bug reported for Business::Stripe::WebCheckout regarding CPAN Ratings.

    This report points out that "CPAN Ratings" no longer exists but it is in my documentation. I didn't explicitly put that in the documentation so it must have been in template or perhaps copied from some other documentation.

    What was CPAN Ratings?
    Why isn't it there anymore?
    Do I need to do anything about it?

CPAN namespace for AI Embedding module in Meditations
1 direct reply — Read more / Contribute
by Bod
on May 28, 2023 at 17:41

    Wise Monks...

    Having written some code that uses Embeddings to compare pieces of text, I feel this would be useful to others. The Embeddings are generated from the OpenAI API at present.

    I plan to package this up into a module for CPAN and would like some advice on the namespace for this module...

    There is already OpenAI::API::Request::Embedding, which is just a thin wrapper to the API. I don't want to use the OpenAI namespace because my module will probably allow other Embedding providers to be used. For example, Hugging Face provides a cheaper but less precise Embeddings API. This may be better suited to some users.

    As well as providing the connection to the API, my module will also have a method to allow two pieces of text to be compared. More functionality than just a thin wrapper.

    I've looked at the AI namespace - e.g. AI::XGBoost. There is also Text::AI::CRM114

    As my module will connect to several different API providers, I am thinking AI::Embedding might be the right name for it but I am not convinced and your opinions and advice would be greatly appreciated.

Searching for homophones and words that are similar in Seekers of Perl Wisdom
3 direct replies — Read more / Contribute
by Bod
on Mar 21, 2023 at 19:45

    Do you know of an existing module or a method of searching for similar words and phrases, especially homophones (words that are spelt differently but pronounced the same)?

    I am planning a tool to analyse blocks of text, for example the 'About' page of a website, and work out the ratio of words like "I", "we", "our" to words like "you" and "your". However, I want the user to be able to enter their company's name and have that included with the first person terms. But variations of the name may exist in the text.
    For example: "Google" could be "Google", "Google Inc", "Google LLC" or "Alphabet" (the last one is not really catchable programmatically)

    Plus, typos exist especially around homophones and I want to be able to catch those in a similar way to search engines say "did you mean"
    For example: "Perl is grate for programmes" should be suggested as "Perl is great for programs".

    For the current use case, only the first example needs to be solved but I'd be interested how you would approach both. Does a long list of homophones need to be referenced? Or perhaps there is already a module on CPAN that deals with this. I have searched but nothing obvious came up.

Create email tracking image in Seekers of Perl Wisdom
7 direct replies — Read more / Contribute
by Bod
on Mar 17, 2023 at 21:17

    I'm looking for a neat way to create a 1x1 transparent PNG to track opening emails. The ways I have been doing it seem overly clumsy.

    The code we use in our main CRM reads an image file and outputs it. This is c2012 code and my coding has improved considerably since then!

    $file='incl/1x1transparent.png'; print "Content-type: image/png\n"; print "Set-Cookie: abc=xxx; SameSite=none; Max-Age=315576000\n" if $us +er; print "\n"; open IMG, $file; binmode IMG; while (sysread(IMG, $buffer,640)) { print $buffer; } close IMG; exit 0;

    I wanted to avoid making an IO call to the filesystem to read in such a small file. My first attempt was to Base64 encode the image and serve that as text but that didn't work...

    So I have come up with this solution:

    print "Content-type: image/png\n\n"; binmode STDOUT; my $image = GD::Image->new(1, 1); my $white = $image->colorAllocate(255,255,255); $image->transparent($white); print $image->png; exit;

    This is better but it seems a bit of overkill to use GD to do this.

    Can you suggest a more elegant solution?

Module callbacks - to fork or not fork in Seekers of Perl Wisdom
3 direct replies — Read more / Contribute
by Bod
on Feb 28, 2023 at 18:10

    I am writing a module to handle Stripe webhook calls. The module does the necessary checking that the call has come from Stripe and provides the user with a means to give defined callbacks for each webhook. They simply pass in a parameter that matches the Stripe event so it is compatible when Stripe adds new events.

    The module is instantiated like this:

    my $stripe = Stripe::Webhook->new( 'signing_secret' => 'whsec_xxxxxxxxxx', 'invoice-paid' => \&paid, 'all-webhooks' => \&all, );
    Here we call &paid; when Stripe sends an invoice.paid event. Plus, we call &all; for every event sent.

    The person using the module could do things that take a significant time in those subs. But the documentation says "your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout.".

    So should I fork to another process for the callback or should I warn the user of the module in the documentation? Something along the lines of: "If your logic might take some time to complete, fork a new process and perform your logic there. This will allow a timely reply to be sent back to Stripe."

    Or should I be dealing with this problem in some other way?

    This is the code that provides the callback:

    my $hook_type = $self->{'webhook'}->{'type'}; $hook_type =~ s/\./-/g; if (defined &{$self->{$hook_type}}) { $self->{'reply'}->{'status'} = 'success'; &{$self->{$hook_type}}($self->{'webhook'}); }

    In my own implementation of handling events from Stripe, I only make a couple of calls to the database and write to a text file. But of course, I have no idea what other people might want to do with the callbacks. They might decide to send an email which is usually not exactly quick...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2023-06-08 14:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you go to conferences?






    Results (32 votes). Check out past polls.

    Notices?