<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" id="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=10.0,initial-scale=1.0" />

    <style>
      html{-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}h1{font-size:1.3em;line-height:1.2;margin:0}ul,ol{margin:0;padding:0}ul li,ol li,li li{margin:0 0 0 36px}[dir=rtl] li{margin:0 18px 0 0}blockquote{border-color:#dfdee1;border-style:solid;border-width:0 0 0 1px;margin:0;padding:0 0 0 1em}[dir=rtl] blockquote,blockquote[dir=rtl]{border-width:0 1px 0 0;padding:0 1em 0 0}pre{font-family:"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;font-size:0.9em;margin:0;padding:1rem;background-color:#f6f5f3;white-space:pre-wrap;word-wrap:break-word;overflow:visible}.message-content{font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";line-height:1.4}.attachment{display:inline-block;margin:0;padding:0}.attachment__caption{padding:0;text-align:center}.attachment__caption a[href]{text-decoration:none;color:#333333}.attachment--preview{width:100%;text-align:center;margin:0.625em 0}.attachment--preview img{border:1px solid #dfdee1;vertical-align:middle;width:auto;max-width:100%;max-height:640px}.attachment--preview .attachment__caption{color:#716d7b;font-size:0.85em;margin-top:0.625em}.attachment--file{color:#282138;line-height:1;margin:0 2px 2px 0;padding:0.4em 1em;border:1px solid #dfdee1;border-radius:5px}.permalink{color:inherit}.txt--xx-small{font-size:14px}.flush{margin:0;padding:0}.push--bottom{margin-bottom:8px}.border--top{border-top:1px solid #ECE9E6}.btn{padding:0.2em 0.4em;font-weight:500;text-decoration:none;border-radius:3rem;white-space:nowrap;background:#5522FA;border-color:#5522FA;color:#ffffff}.btn--email{display:inline-block;text-align:center;font-weight:500;font-size:1em;text-decoration:none;border-radius:2em;white-space:nowrap;background:#5522FA;border-color:#5522FA;color:#ffffff;border-top:0.3em solid #5522FA;border-left:1em solid #5522FA;border-bottom:0.3em solid #5522FA;border-right:1em solid #5522FA}

    </style>
  </head>

  <body>
    <div class="message-content">
      <div class="trix-content">
  <div>Hi Tim<br><br>I think you may have had your specific problem addressed. But the idea of code and data sometimes aligning in this way to raise problems does strike a chord with me.<br><br>Apologies that this is quite probably completely tangential to what you are talking about. I just got the hint that you may be thinking of this class of problems. (If this is what you mean, I also can't think of a name for it)<br><br>I've worked on a project that could in simple terms be defined as a CMS builder.<br>Not a CMS, but a layer above that.  A tool that can build branded CMS's.<br><br>The real eye opening moment for me was when the developer who had worked on it for the longest and helped me set up my local environment. He had finally helped get me through all the configuration and setup errors.<br><br>Then I hit a completely empty white screen. <br><br>'And there you have it. Done.' he said. He was deadly serious, but I definitely interpreted it as a deadpan joke.<br><br>It was completely set up and ready to go in his eyes.  Obvious to him, was that I just needed all the right data in the database for it to look anything like one of the production websites.<br>But that's "Just Data".<br><br>This disparity between production, development and test was all because it's all "Just Data".<br>We managed to write a few scripts that would allow us to pull down realistic sets of seed data, so we could at least test with a reasonable amount of confidence, that certain key sets of "Just Data" could reliably continue to work if any of the code changed.<br><br>These types of generic upon generic solutions can sometimes bite us in terms of the trade off of flexibility and complexity. It is sometimes mind-melting to work at extreme levels of generic abstractions. And sometimes we, rightly so, crave something simpler and dare I say, more hard-coded.<br>It doesn't mean there's no business in building CMS building websites and API's. It's just it can be a lot of overhead if really you just want a few specifically branded CMS's.<br><br>It can really be simpler to lock yourself into more specific non-generic concepts. At least for the first few iterations. You lose the flexibility of having users editing all your persisted domain specific data in the UI. And you have an overhead of needing a code change in order to perform any CRUD on one of these entities. But whilst you crystallize requirements, this can be valuable. And the trade-off becomes clear in terms of amount of time spent doing code change deploys for something that could be made user-CRUD-able. <br><br>And when I'm talking about not locking yourself into generic concepts early on, I'm saying things like: "don't build user-configurable colour schemes, layouts and themes into your user settings screen, when you don't know if your users even use the settings screen". Rather than "hardcode all your user email addresses". <br><br>I think the only way to sensibly draw the line between these things is in terms of deltas of change per unit of time. How many times per year does/should this happen being a rough rule of thumb for knowing whether or not you want to do a deploy of code vs want to maintain a UI or similar to manage it.<br><br>Transactional things like users and their email addresses have a clear place in the data zone.<br><br>Semi-transactional things like 'client website' may have some kind of middle ground position. Yes you may need your site builder to work generically for any client and not lock yourself into any specific client. But you really also need your site to definitely work for your only two clients that exist. Maybe sometimes it's worth hardcoding things related to these records. Like writing code in tests around them.<br> <br>Really abstract things _can_ be handled as data. But are sometimes best kept as code. Like you probably shouldn't build a 'RequestsController' that creates and shows 'Request' records and routes all the requests in your application to data backed views that have data persisted to represent all the routes in your application. Yes it's flexible. But it's almost certainly not the thing you want to build.<br><br>But sometimes the hardcoding trade-off is worth it. Like "hardcode the theme and layout". Start building your domain model using a "User" with an "email" instead of an "AuthenticatableEntity" with an "access_token_ownership_identifier_id" that joins to a table with a single entry of "email". Or even set the price for the subscription we sell in the code.<br><br>The more 'hardcoding' you do, the more you find that test setup is simpler. It's cheaper in terms of test run-time. It is more realistic. And you will be less likely to alter code that breaks one of these "Just Data" setups. And because maybe that aspect of the data really doesn't need to change more than once a year.<br><br>Sometimes the data is 'the name of the subscription'. And you have 3 subscriptions on Stripe, and it doesn't matter that you're displaying one specific one of these a certain way and hardcoding it, because you're almost never going to change the names. And it's not worth building the CRUD UI that makes the stripe subscriptions configurable on the website, because that's a bunch of data syncing and API integration for something that you have no intention of changing very often.<br><br>I believe that a key component of a successful "Just Data" setup is being able to get development and test environments rapidly in sync with production in a way that will give you confidence that there's no data and code interplay that could only happen in one of these "Just Data" environments.<br>E.g. a rake task that will update some JSON fixtures, or update local records in your db.<br><br>The worst case for the hardcoding approach is to regularly need developer time and code deploys for trivial data changes.<br><br>The worst case for the generic approach is to have 'tenant/client specific' setup that is difficult to easily replicate in tests and that results in client sites breaking in production, and that turns out to not even vary across clients. I.e. all the mental overhead of the generic solutions where more concrete concepts could have been locked down earlier and been easier to maintain from the beginning.<br><br>Everything we do is either data or code. But there are extremes at either end we can spend more time on than we gain any benefit from.<br><br>Anyway, apologies that this doesn't really address your issue. I'd imagine that there's some of the data model that you have there, that may make sense to continue to hard code, and I think sometimes that's fine. <br><br>But obviously: It all depends.<br><br><br><br></div><div>On June 22, 2021, Andrew Donovan <andrew_donovan@hotmail.com> wrote:</div><blockquote><style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
span.EmailStyle18
        {mso-style-type:personal-reply;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;
        font-family:"Calibri",sans-serif;
        mso-fareast-language:EN-US;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
/* List Definitions */
@list l0
        {mso-list-id:1819221820;
        mso-list-type:hybrid;
        mso-list-template-ids:-76115780 134807569 134807577 134807579 134807567 134807577 134807579 134807567 134807577 134807579;}
@list l0:level1
        {mso-level-text:"%1\)";
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-18.0pt;}
@list l0:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-18.0pt;}
@list l0:level3
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
@list l0:level4
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-18.0pt;}
@list l0:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-18.0pt;}
@list l0:level6
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
@list l0:level7
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-18.0pt;}
@list l0:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-18.0pt;}
@list l0:level9
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
ol
        {margin-bottom:0cm;}
ul
        {margin-bottom:0cm;}
--></style><div style="word-wrap:break-word" class="__body">
<div class="WordSection1">
<p class="MsoNormal"><span style="mso-fareast-language:EN-US">Hi Tim,</span></p>
<p class="MsoNormal"><span style="mso-fareast-language:EN-US">Your db or permanent storage is your source of truth , if you’re using Rails then use all the facilities it offers such as ActiveRecord. If Product is somehow defining prices and other behaviour
 for your digital items then it needs to be modelled in the db. If you don’t have a db schema I’d draw out a simply entity relationship diagram which should help in identifying where things are missing or don’t tie up. From the gist of the details below it
 sounds like you have access to a database. I’d be wary of using code for defining product strategy, amongst other things how do you record product changes over time ?
</span></p>
<p class="MsoNormal"><span style="mso-fareast-language:EN-US">Regards</span></p>
<p class="MsoNormal"><span style="mso-fareast-language:EN-US">Andrew</span></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"><span style="mso-fareast-language:EN-US">Regards</span></p>
<p class="MsoNormal"><span style="mso-fareast-language:EN-US">Andrew</span></p>
<p class="MsoNormal"><span style="mso-fareast-language:EN-US"> </span></p>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0cm 0cm 0cm">
<p class="MsoNormal"><b><span>From:</span></b><span> Chat <chat-bounces@lists.lrug.org>
<b>On Behalf Of </b>Tim Cowlishaw<br>
<b>Sent:</b> 22 June 2021 15:43<br>
<b>To:</b> Ruby Group <chat@lists.lrug.org><br>
<b>Subject:</b> [LRUG] Objects that are neither classes nor records b.ut something in between</span></p>
</div>
<p class="MsoNormal"> </p>
<div>
<div>
<p class="MsoNormal">Hi there folks! Hope you're all doing well.</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">I've just run into a problem / conundrum / anti-pattern / code smell that seems to recur fairly frequently in projects I've worked on of late, and which i'm not really sure how to name in order to search for an existing solution, so i figured
 I'd post it here in case anyone has some sensible advice, or in the hope that it provokes some interesting discussion, at least.</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">The problem basically arises when there's some 'object' in my system (using this word in the vaguest possible sense for now) which has both *data* (so fields that can be updated through an API or webapp, and which should probably be stored
 in a database row), but also has a one-to-one correspondence with a specific bit of behaviour, expressed as code.</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">An example:</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">I'm currently working on  a project which is a digital library. It contains lots of Books:</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">class Book < Struct.new(:id, :title, :author, :content); end</span></p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">Users can buy access to these books either individually (all books are the same price), or by buying a subscription to the whole library. Forgetting the DB / ORM side of this for a sec, i'd model a toy implementation of what i want to do
 something like the following:</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">class User</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def initialize(purchases=[])</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    @purchases = purchases</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   attr_reader :purchases</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   def has_access_to?(book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">     purchases.any? { |p| p.grants_access_to?(book) }</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">class Purchase < Struct.new(:product)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def grants_access_to?(book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    product.grants_access_to?(book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end </span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">class Product</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def self.price</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   raise NotImplementedError</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def </span><a href="http://self.name"><span style="font-family:"Courier New"">self.name</span></a></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    raise NotImplementedError</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def grants_access_to?(book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    raise NotImplementedError</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">class OneOffPurchase < Purchase</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def initialize(book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    @book = book</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  attr_reader :book</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def self.price</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    500</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def </span><a href="http://self.name"><span style="font-family:"Courier New"">self.name</span></a></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    "A single book"</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def grants_access_to?(other_book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    </span><a href="http://book.id"><span style="font-family:"Courier New"">book.id</span></a><span style="font-family:"Courier New""> ==
</span><a href="http://other_book.id"><span style="font-family:"Courier New"">other_book.id</span></a></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">def Subscription < Purchase</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def self.price</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    500</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def </span><a href="http://self.name"><span style="font-family:"Courier New"">self.name</span></a></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    "All inclusive special subscription"</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def grants_access_to?(book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    true</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal">end</p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">This all to me looks reasonably sensible so far, but i also want those product names and prices to be editable by site admins through our 'backoffice' webapp, so they need to be stored in the DB, and this is where I have trouble finding
 a solution i'm happy with. Naively I could have an ActiveRecord type object and a fairly simple dispatch mechanism to the code that works out the permissions, but this has some fairly obvious flaws:</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">class Product < Struct.new(:unique_key, :name, :price, :book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">  def grants_accesss_to?(other_book)</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">     if unique_key = :one_off && book.present?</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">       </span><a href="http://book.id"><span style="font-family:"Courier New"">book.id</span></a><span style="font-family:"Courier New""> ==
</span><a href="http://other_book.id"><span style="font-family:"Courier New"">other_book.id</span></a></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   elsif unique_key == :one_off</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">      raise "oops! we're in a totally inconsistent state</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    elsif unique_key == :subscription && book.present?</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">       raise "yep, this makes absolutely no sense either"</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    elsif unique_key == :subscription</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">      true</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">   else</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">     raise "this isn't even a real product type. hopeless."</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">    end</span></p>
</div>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:"Courier New"">end</span></p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">aside from all the brittleness and smells that you can see above, this is also kinda useless in a bunch of respects - we can't add new product types without making a change to the codebase, we have to do a bunch of convoluted validation
 to avoid all the various possible inconsistent states we can get into above, and our codebase is tightly coupled to the value of that unique_key database field, which i'm very suspicious of?</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">Therefore, anyone got any smart ideas about how to do this better? The requirements I'm looking to fulfil, in summary:</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">1) A  product has a specific 'strategy' for granting access to a book for a user, expressed as  ruby code</p>
</div>
<div>
<p class="MsoNormal">2) A product has a price and name that is editable as data through our web app's admin interface</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">And, in general, i'd be interested to know -  is there a name for this type of (anti-)pattern - Types of things that refuse to sensibly sit in the domain of software classes or Database fields? Does any of this even make any sense? I'm
 not so sure myself anymore.</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">Any thoughts gratefully received!</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">Cheers,</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
<div>
<p class="MsoNormal">Tim</p>
</div>
<div>
<p class="MsoNormal"> </p>
</div>
</div>
</div>


</div><br><br><div>_______________________________________________<br>Chat mailing list<br>Chat@lists.lrug.org<br>Archives: http://lists.lrug.org/pipermail/chat-lrug.org<br>Manage your subscription: http://lists.lrug.org/options.cgi/chat-lrug.org<br>List info: http://lists.lrug.org/listinfo.cgi/chat-lrug.org</div></blockquote>
</div>


    </div>
  </body>
</html>