On 10/27/2016 06:55 PM, Bryan Richter wrote:
> Hi everyone,
> The crowdmatch mechanism code is still a work in progress, and I want
> to talk about what I've done so far to get some feedback. This email
> has become rather long! Feel free to skim it to find parts that sound
> interesting. This was written for my benefit as much as yours, and in
> the hope that I get some feedback. Anything at all, like "Well hurry up
> and do it already", is very welcome.
> It is also written for posterity and for your amusement. :)
> The new code is on a branch called split-mechanism. A giant comparison
> can be viewed, but it's longer than this email(!) and I'm going to
> call out specific items instead. This will be a technical discussion,
> but not too nutty. I will talk about how I structured the crowdmatch
> mechanism code, how I've designed it to be used, how I've designed it
> to be testable, and how some quirks popped up during my headlong crash
> through coding it. Please reply with feedback ABOUT ANY PART OF THIS! :D
> ### Table of Contents:
> 1. A separate module for the crowdmatch mechanism
> 2. API for the library
> 2.a. Client code requirements
> 2.b. Usage example
> 2.c. Exported actions
> 3. Database management
> 3.a. Migrations
> 3.b. The test database
> 3.c. run-persist library
> 4. Implementation to allow property testing
> 5. Implementation to allow more-than-one Stripe
> 6. Difference between MechAction and StripeI
> 7. What's left to do
> Appendix A: Questions
> ### 1. A separate module for the crowdmatch mechanism
> Although there is only one foreseeable client to this code, namely the
> Snowdrift website, I went ahead and structured it as a standalone
> library. It is in a directory called 'crowdmatch'. I did this so it can
> be tested independently. It will be a lot more stable than the website
> eventually, too, which is a good decider for whether something should
> be independent or not.
> ### 2. API for the library
> The crowdmatch library puts certain constraints on client code because
> it requires a database. Then, it does what any API does: provides
> actions and data types.
> ### 2.a. Client code requirements
> A client of this library must be using Postgres via Persistent.
> The only way to be agnostic about databases would be to completely
> reimplement a database. That's nuts. The library will just use Postgres,
> and thus requires client code to use it as well.
> The library also needs to do IO to talk to Stripe. Rather than rely
> on monad transformers or mtl-style classes to manage these different
> layers, I use plain old functions. Each API method needs to be passed
> a handler for running database actions. Will this be a pain in the
> ass? Probably not. It's also the easiest style to change FROM, should
> that become necessary.
> The library maintains its own notion of what a "Patron" is. To
> interface with the website, I created ToMechPatron and FromMechPatron
> type classes. These are easy to define for the User type. Thus, it's
> easy to create a relationship between "website Users" and "crowdmatch
> ### 2.b. Usage example
> Here's a usage example that covers these parts so far.
> :: (ToMechPatron usr, MonadIO io, MonadIO env)
> => SqlRunner io env
> -> usr
> -> env Patron
> You can run:
> someHandler = do
> Entity uid user <- requireAuth
> patron <- Crowdmatch.fetchPatron runDB uid
> See how 'runDB' is passed in as the database runner.
> ### 2.c. Exported actions
> There are about ten operations that will be exported, as well as a
> handful of data types. I am starting with fetching, storing, and
> deleting two items: payment-method tokens and pledges. The payment
> tokens are what Stripe gives us in lieu of credit card info. Pledges are
> what you think they are: the record of a donor pledged to a project.
> Next in the API are two super important methods (prototyped but not
> implemented yet): 'runCrowdmatch' and 'processPayments'.
> The first looks at all pledged patrons and calculates an outstanding
> donation balance. After a crowdmatch event, each pledged patron will
> then "owe" a certain amount to the project. That all happens in the
> database in a single transaction.
> Later, at our leisure, we can run 'processPayments'. That method
> inspects outstanding balances and sends payment commands via
> Stripe. This is where fee limits take effect.
> The website won't trigger the crowdmatch event or payment processing.
> Instead I'll have two simple utilities that do that stuff. I'll just
> run them manually at first.
> This section was longer than I intended, but that's probably good.
> It's one of the more important. Feedback on the API is highly welcome.
> API to date: