<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="http://www.ericschaefer.org"  xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>Eric Schäfer&#039;s Blog</title>
 <link>http://www.ericschaefer.org</link>
 <description></description>
 <language>en</language>
<item>
 <title>Database tests</title>
 <link>http://www.ericschaefer.org/database-tests</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Database tests are a type of automated tests that do not get enough love these days. Everybody is talking about unit tests, acceptance tests, integration and regression tests.&lt;/p&gt;
&lt;p&gt;Database tests have gotten a bad reputation because of supposed unit tests which test functionality through the DB. They pretend to test some kind of model by reading from and writing to the DB. Those are not database tests (and those are not models, by the way). So what is a database test and where does it fit in?&lt;/p&gt;
&lt;p&gt;When I have to interact with a database I want to make sure the schema of that DB is formed as expected. The design and implementation of that schema is usually not driven by tests. In test driven design external services like databases are usually mocked, because they are not part of the application. In many organisations the schema might even be designed by a different department and may be subject to regulations. No matter where the design comes from, it might be a good idea to have tests for it.&lt;/p&gt;
&lt;p&gt;A database test should be testing if the schema of your database is conforming to your expectations. It can be implemented on two different layers of your database abstraction. Either it uses raw SQL instruction or it uses some kind of data access object of your application. In either case it should only test simple CRUD functionality. The test are supposed to tell us if we can add, read, update and delete rows. If we rely on other database features like auto-increment indexes or triggers then we should also test if those are in place.&lt;/p&gt;
&lt;p&gt;Such tests help us determine if somebody changed the schema without updating the code and vice versa. They tell us if Bob ‘optimized’ the database and they tell us if the schema has been adapted to Alices changes in the code. Instead of a large number of tests for business objects breaking, because they were tested through the database we now have tests that pinpoint the source of the problem. &lt;/p&gt;
&lt;p&gt;Now that we have our schema tests place, when are they run? If they are reasonably fast and the database is accessible in your development environment then you can run them in your usual TDD/BDD cycle like most of your tests. If they are rather slow or access to the database is non-existent or flaky in your dev system then do not run them in that cycle. You want that cycle to be fast and reliable. Instead you could run them on your CI (continuous integration) server. They can even be run on different deployment stages to identify if database changes have been deployed as well. No more forgetting to apply your schema changes to the production database. For running them selectively you need to make sure they are set up to enable that. E.g. put them in a different folder from our other tests or mark them as DB tests if your testing framework supports that and exclude them where you do not want them to run.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Mon, 06 Apr 2015 12:03:34 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">9 at http://www.ericschaefer.org</guid>
</item>
<item>
 <title>Expecting Exceptions</title>
 <link>http://www.ericschaefer.org/expecting-exceptions</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Most test tools provide the developer with means to suggest that a test should expect an specific exception. For instance, JUnit the prevalent test tool for the Java language provides an annotation to mark such a test (from the JUnit 4 documentation):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test(expected=IndexOutOfBoundsException.class) &lt;br /&gt;    public void empty() { &lt;br /&gt;         new ArrayList&amp;lt;Object&amp;gt;().get(0); &lt;br /&gt;    }&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;The annotation marks the method as a test and the parameter “expected” tells JUnit which exception type to expect.&lt;/p&gt;
&lt;p&gt;Recently I stumbled about some test code from a co-worker who did the tests for expected exceptions a little different:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test&lt;br /&gt;    public void emptyToo() {&lt;br /&gt;        try {&lt;br /&gt;            new ArrayList&amp;lt;Object&amp;gt;().get(0);&lt;br /&gt;        }&lt;br /&gt;        catch (IndexOutOfBoundsException e) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;        catch (Exception e) {&lt;br /&gt;            Assert.fail(&quot;Wrong Exception raised: &quot; + e);&lt;br /&gt;        }&lt;br /&gt;        Assert.fail(&quot;No Exception raised.&quot;);&lt;br /&gt;    }&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;This code does the same as the first one. I asked him why he did it this way instead of the more succinct “canonical” way. I thought maybe he does not know about it. But his answer baffled me. &lt;/p&gt;
&lt;p&gt;He argued that tests are living documentation of the usage of the system under test. If somebody would come to this test to see which exceptions it would throw under which circumstances they could use his test code as a template for how to deal with the exceptions. In extreme cases they could even copy the test code verbatim, more or less, if they needed to catch the exceptions at this level.&lt;/p&gt;
&lt;p&gt;I have never seen it this way. He is right somehow. There is one thing which bothers me with this solution though. The actual exercise of the system under test is buried deeply inside the try/catch construct. Tests are supposed to be easy to read, but in this case it is not immediately obvious what this test is doing. I would rather have the test tell me right away that it is asserting that an exception is raised when the system under test is exercised with the current setup. The canonical version is explicit about that. Before I even get to read the method name I will know that it is expecting an IndexOutOfBoundsException. The reader of the method will know it and, which is important too, the test framework will know it. The Assert.fail() calls do not really tell the story of the test in a way JUnit can understand. It only sees that some test failure occurred.&lt;/p&gt;
&lt;p&gt;Still I like the idea. This might be useful for more complex integration tests...&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Sun, 04 May 2014 15:03:20 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">8 at http://www.ericschaefer.org</guid>
 <comments>http://www.ericschaefer.org/expecting-exceptions#comments</comments>
</item>
<item>
 <title>Separating the Application from the Environment</title>
 <link>http://www.ericschaefer.org/separating-the-application-from-the-environment</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;A useful pattern for developing testable rich client GUI applications is the *[Humble Dialog Box](&lt;a href=&quot;http://c2.com/cgi/wiki?TheHumbleDialogBox&quot;&gt;http://c2.com/cgi/wiki?TheHumbleDialogBox&lt;/a&gt;)* by Michael Feathers. I have used variations of it for *[TDD](&lt;a href=&quot;http://c2.com/cgi/wiki?TestDrivenDevelopment&quot;&gt;http://c2.com/cgi/wiki?TestDrivenDevelopment&lt;/a&gt; &quot;Test Driven Development&quot;)* style development of all kinds of Windows programs. It boils down to have a rather dumb view (the humble dialog box) that delegates the handling of its events to a controller object. The controller does all the magic. It talks to the models and does whatever is necessary to react to the users gestures. When it is done it tells the view how to change its state. The view is mostly passive in this process. It receives events (user gestures), delegates them to the controller and receives updated data. It usually does not contain any logic besides what is needed to retrieve any event related data to hand it over to the controller. The controller talks to the view only through an interface. It does not know the concrete implementation of it. &lt;/p&gt;
&lt;p&gt;With this approach it it possible to switch to a different GUI library or support multiple libraries in parallel without changing the controller. Use of this pattern enabled me to drive the design of the controllers and models by tests. It also let me migrate parts of an application from the older .Net Windows-Forms (SWF) to the more modern WPF library without even touching the controllers. All I needed to do was creating new WPF views, which called the same services of the controllers as their old SWF counterparts did. The controller would not know the difference as it did not know the difference between the tests that drove its design and the &quot;real&quot; views. Of course this also lets you use different GUI libraries &quot;in parallel&quot;. The controllers and models are implemented once and can be used with different GUI libraries on different platforms. &lt;/p&gt;
&lt;p&gt;For Web development I usually follow the *[Ports and Adapters](&lt;a href=&quot;http://alistair.cockburn.us/Hexagonal+architecture&quot;&gt;http://alistair.cockburn.us/Hexagonal+architecture&lt;/a&gt;)* pattern by Alistair Cockburn. Instead of having horizontal Layers stacked upon each other, having the user interface on top and the database on the bottom it puts the business layer(s) in the center and services like the UI and the DB on the outside. For each service need of the application there is a port and adapters that plug concrete services into those ports. E.g. there might be a persistence port and an MySQL adapter or an UI port and a Ruby on Rails adapter. A persistence adapter might be a database specific *[Repository](&lt;a href=&quot;http://martinfowler.com/eaaCatalog/repository.html&quot;&gt;http://martinfowler.com/eaaCatalog/repository.html&lt;/a&gt;)* and a UI adapter would be a controller in an MVC framework. There are multiple architectural patterns that are very similar. [The Clean Architecture](&lt;a href=&quot;http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html&quot;&gt;http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html&lt;/a&gt;) by Robert Martin explains them in more detail.&lt;/p&gt;
&lt;p&gt;Lately I was thinking about how I would implement *Ports and Adapters* with regards to rich client GUI applications and how it relates to the *Humble Dialog Box* and then it dawned on me. The *&quot;Controller&quot;* in the *Humble Dialog Box* is actually the application specific business logic, the *Interactor*, the *Control Object*. The views &quot;code behind&quot; is the UI ports adapter for the used GUI library. The term &quot;Controller&quot; was confusing me in this context, because with the usual MVC web frameworks I use the controller only as an adapter to the business logic. It does not contain any business logic itself.&lt;/p&gt;
&lt;p&gt;It basically always comes back to separating the application from the environment. The details of the different approaches might differ, but the big picture is always the same.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Sun, 27 Oct 2013 11:38:41 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">7 at http://www.ericschaefer.org</guid>
 <comments>http://www.ericschaefer.org/separating-the-application-from-the-environment#comments</comments>
</item>
<item>
 <title>A Generic WPF-Control</title>
 <link>http://www.ericschaefer.org/a-generic-wpf-control</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;There are several ways to reference generic types from XAML for bindings. Unfortunately there is no way to &lt;em&gt;define&lt;/em&gt; a generic type using XAML.&lt;/p&gt;
&lt;p&gt;I needed to port a fancy dialog for selecting from a list of items from Winforms to WPF. The original dialog had a generic type parameter for the type of the items. Something along the lines of &lt;code&gt;public class SelectionDialog&amp;lt;TItem&amp;gt; : Form&lt;/code&gt;. This is not possible if you want to design the layout of a control with XAML, because the XAML type definition does not allow to define type parameters.&lt;/p&gt;
&lt;p&gt;All I did to circumvent this limitation was to create a non generic dialog class which I designed with the Visual Studio XAML designer. Then I created a second class with a type parameter for the items. This class is derived from the XAML designed class and voilà I got a XAML dialog with a type parameter.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Mon, 21 Nov 2011 20:32:06 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">6 at http://www.ericschaefer.org</guid>
 <comments>http://www.ericschaefer.org/a-generic-wpf-control#comments</comments>
</item>
<item>
 <title>Custom Field Handlers for Views 2 (Drupal)</title>
 <link>http://www.ericschaefer.org/blog/2011/01/09/custom-field-handlers-for-views-2-drupal</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;This is a step-by-step example how to create custom field handlers for &lt;a href=&quot;http://www.drupal.org/project/views&quot;&gt;Views 2&lt;/a&gt; for &lt;a href=&quot;http://www.drupal.org&quot;&gt;Drupal&lt;/a&gt;. I needed this information myself and &lt;a href=&quot;http://views-help.doc.logrus.com/&quot;&gt;all I could find&lt;/a&gt; was not going very deep, at least not as deep as I needed it. I learned all the necessary details by reading other handlers that came with the views module (in views/modules) and by extensive use of &lt;code&gt;print_r()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is the goal: Extending the &lt;a href=&quot;http://www.drupal.org/project/scheduler&quot;&gt;scheduler module&lt;/a&gt; to be able to view a countdown until a node is published or unpublished. Countdown meaning &quot;How many time units until the node is published or unpublished&quot;. This would be the perfect job for the views module. Scheduler already exposes its publish_on and unpublish_on fields for use with views.&lt;/p&gt;
&lt;p&gt;Defining the supported views version in file scheduler.module:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;scheduler_views_api&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;() {&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$info&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;api&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  return &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$info&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Defining the table and fields in file scheduler.views.inc:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;scheduler_views_data&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;() {&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;table&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;group&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #FF8000&quot;&gt;// how is the scheduler table linked to the nodes&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;table&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;join&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;node&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;left_field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;nid&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;nid&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #FF8000&quot;&gt;// description of the fields (table columns)&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;publish_on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Publish on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;help&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Date/time on which the article will be automatically published&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_field_date&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;click sortable&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;TRUE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;filter&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_filter_date&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;label&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Publish on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;sort&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_sort_date&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;unpublish_on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Unpublish on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;help&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Date/time on which the article will be automatically unpublished&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_field_date&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;click sortable&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;TRUE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;filter&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_filter_date&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;label&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Unpublish on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;sort&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_sort_date&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;  return &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;I am not going to explain this code in detail because it is really simple and everything is documented really nice in the &lt;a href=&quot;http://views-help.doc.logrus.com/&quot;&gt;views help&lt;/a&gt;. Since we just exposed existing DB fields we only needed to define how the scheduler table is to be joined with the node table. For displaying the values we could rely on a predefined handler for date values (&lt;code&gt;views_handler_field_date&lt;/code&gt;). That is the code that we need to build upon.&lt;/p&gt;
&lt;p&gt;What we need now is a custom handler because there is no predefined handler for our purpose. At first we need to define the field. Let&#039;s start with a simple second count down:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  $tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;publish_countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Publish countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;help&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Time until the article will be automatically published&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler_handler_field_scheduler_countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;click sortable&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;FALSE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;timestamp_field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;publish_on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;  );&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Note the handler definition (&lt;code&gt;scheduler_handler_field_scheduler_countdown&lt;/code&gt;). Now we need to actually implement the handler. Handlers need to live in their own files. So we create a file scheduler_handler_field_scheduler_countdown.inc:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;class &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;scheduler_handler_field_scheduler_countdown &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;extends &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;views_handler_field &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;{&lt;br /&gt;  function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;() {&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;ensure_my_table&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;();&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;node_table &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;ensure_table&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;node&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;relationship&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;field_alias &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;add_field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;IF(publish_on AND publish_on &amp;gt; UNIX_TIMESTAMP(),&lt;br /&gt;        publish_on - UNIX_TIMESTAMP(), NULL)&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;table_alias &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;. &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;_&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;. &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;This handler class derives from &lt;code&gt;views_handler_field&lt;/code&gt;, the base class for field handlers. All we need to do is override the &lt;code&gt;query()&lt;/code&gt; method, because the countdown value can be calculated via the query. The add field method of the query adds a new field to the query (dooh!). We need a field in the query result that is calculated like this: &lt;code&gt;publish_on - UNIX_TIMESTAMP()&lt;/code&gt;. The rest of the condition statement is to limit the calculation to time stamps that represent future times. This is the actual query that will be performed to determine the fields for a view containing the countdown:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT node.nid AS nid,&lt;br /&gt;       node.title AS node_title,&lt;br /&gt;       IF(publish_on AND publish_on &amp;gt; UNIX_TIMESTAMP(),&lt;br /&gt;           publish_on - UNIX_TIMESTAMP(),&lt;br /&gt;           NULL) AS scheduler_publish_countdown&lt;br /&gt;    FROM node node &lt;br /&gt;    LEFT JOIN scheduler scheduler ON node.nid = scheduler.nid&lt;br /&gt;    WHERE node.status = 0&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;We are not done yet. All handlers must be declared in the &lt;code&gt;_views_handlers()&lt;/code&gt; hook which can be implemented in the module.views.inc file. So we extend scheduler.views.inc with this function: &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;scheduler_views_handlers&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;() {&lt;br /&gt;  return array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handlers&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler_handler_field_scheduler_countdown&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;parent&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;views_handler_field&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      )&lt;br /&gt;    )&lt;br /&gt;  );&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;That&#039;s all for a simple countdown field. It can be added to views and shows the number of seconds until a node will be published. Our handler only supports a countdown for the publish_on field because that&#039;s hard coded into our query. We can provide all the additional parameters we need in the &#039;field&#039; array:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  $tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;publish_countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Publish countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;help&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Time until the article will be automatically published&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler_handler_field_scheduler_countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;click sortable&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;FALSE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;timestamp_field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;publish_on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;  );&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Now we can change the handler like this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;    $time_field &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;definition&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;timestamp_field&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;];&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;field_alias &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;add_field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;IF(&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$timestamp_field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039; AND &#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$time_field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039; &amp;gt; UNIX_TIMESTAMP(),&lt;br /&gt;        &#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$time_field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039; - UNIX_TIMESTAMP(), NULL)&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;table_alias &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;. &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;_&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;. &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;field&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Since the name of the actual field is now variable we can add a field for a unpublish countdown:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  $tables&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;unpublish_countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Unpublish second countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;help&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Time until the article will be automatically unpublished&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;handler&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scheduler_handler_field_scheduler_countdown&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;click sortable&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;FALSE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;timestamp_field&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;unpublish_on&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;  );&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;If scheduler will support scheduled promotion/demotion of nodes or any other action, we can add countdown fields really easy.&lt;/p&gt;
&lt;p&gt;But we still only have second countdowns which are actually not really that useful. We would like to have all kinds of countdowns, like in minutes, hours, days and weeks. But we are not going to create handlers for all those values and a fixed definition of fields for those values is also not so nice. How about making the field definition and the handler more generic regarding the actual display format of the countdowns. That&#039;s where field options come in handy.&lt;/p&gt;
&lt;p&gt;Until now the custom field handler is pretty simple because it only manipulates the database query to provide derived fields. Now we want to alter the rendering of the fields. The user should be able to select how the countdown should be displayed, e.g. its units of scale. It should also be possible to select how the unit should be displayed. At first we need to define the options form in the &lt;code&gt;options_form()&lt;/code&gt; function of the handler. We want the user to able to select seconds, minutes, hours, days or weeks as unit of scale and we also want a smart mode which figures out which unit would be best for the current value of the countdown. For the display of the units the user should be able to select long units (e.g. &quot;4 days&quot; or &quot;6 weeks&quot;), short units (e.g. &quot;4d&quot; or &quot;6w&quot;) or no units at all.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;options_form&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$form&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;, &amp;amp;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$form_state&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;parent&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;options_form&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$form&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$form_state&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$form&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;countdown_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Display countdown as&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#type&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;radios&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#options&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;smart&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Smart mode&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;seconds&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Seconds&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;minutes&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Minutes&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;hours&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Hours&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;days&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Days&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;weeks&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Weeks&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;      ),&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#default_value&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;countdown_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;],&lt;br /&gt;    );&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$form&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;units_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#title&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Display time units&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#type&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;radios&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#options&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;long&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Long (e.g. 3 days)&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;short&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;Short (e.g. 3d)&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;none&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;No units at all&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;),&lt;br /&gt;    ),&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;#default_value&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;units_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;],&lt;br /&gt;    );&lt;br /&gt;  }&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;This code defines two option fields as radio buttons. We need to tell &lt;em&gt;views&lt;/em&gt; that there will be two options with default values:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;option_definition&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;() {&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$options &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;parent&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;option_definition&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;();&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;countdown_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;default&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;smart&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;units_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;] = array(&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;default&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;long&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;);&lt;br /&gt;    return &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  }&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Now the user will be presented this two options, but they will still have no effect. We need to define how the field values will be rendered. A field handler can define a &lt;code&gt;render()&lt;/code&gt; function for this task. If there is no such function a value will be displayed as the database query returns it.&lt;/p&gt;
&lt;p&gt;Let&#039;s define the scale values and the unit names first:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;SECOND_SCALE &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  const &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;MINUTE_SCALE &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;60&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  const &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;HOUR_SCALE &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;3600&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  const &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;DAY_SCALE &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;86400&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  const &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;WEEK_SCALE &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;604800&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;&lt;br /&gt;  private &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$render_params &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= array(&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;seconds&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scale&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;SECOND_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;singular&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;second&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;plural&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;seconds&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;abbreviated&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;s&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;minutes&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scale&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;MINUTE_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;singular&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;minute&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;plural&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;minutes&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;abbreviated&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;min&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;hours&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scale&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;HOUR_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;singular&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;hour&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;plural&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;hours&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;abbreviated&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;h&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;days&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scale&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;DAY_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;singular&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;day&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;plural&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;days&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;abbreviated&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;d&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;weeks&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; array(&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scale&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;WEEK_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;singular&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;week&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;plural&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;weeks&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;      &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;abbreviated&#039; &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;=&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;w&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;,&lt;br /&gt;    ),&lt;br /&gt;  );&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Because we defined the above array of unit properties we do not need a switch-statement for the render function:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;  &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$values&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;countdown_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;];&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$values&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;{&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;field_alias&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;};&lt;br /&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #FF8000&quot;&gt;# what about the &quot;smart&quot; scale?&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$params &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;render_params&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;];&lt;br /&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$scaled_value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;round&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;/ &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$params&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;scale&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;]);&lt;br /&gt;    switch (&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;units_display&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;]) {&lt;br /&gt;      case &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;long&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;:&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$rendered_value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;format_plural&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$scaled_value&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;1 &#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$params&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;singular&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;],&lt;br /&gt;                            &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;@count &#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$params&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;plural&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;]);&lt;br /&gt;        break;&lt;br /&gt;      case &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;short&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;:&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$rendered_value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$scaled_value&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$params&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;abbreviated&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;];&lt;br /&gt;        break;&lt;br /&gt;      case &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;none&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;:&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$rendered_value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$scaled_value&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;        break;&lt;br /&gt;    }&lt;br /&gt;    return &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$rendered_value&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;  }&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;This code does not handle the &quot;smart&quot; scale option yet. We need to examine the actual value and check if it is larger than the scale value of each unit. We want to use the largest unit possible. E.g. if the value is 30 we want seconds as the unit, if the value is 61 we want minutes as the unit (and so on). Just replace the comment in the code above with the code below:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #000000&quot;&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;&amp;lt;?php&lt;br /&gt;    &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;if (&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;== &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;smart&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;      if (&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;WEEK_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;weeks&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;      }&lt;br /&gt;      elseif(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;DAY_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;days&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;      }&lt;br /&gt;      elseif(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;HOUR_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;hours&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;      }&lt;br /&gt;      elseif(&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$value &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;MINUTE_SCALE&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;) {&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;minutes&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;      }&lt;br /&gt;      else {&lt;br /&gt;        &lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;$countdown_display &lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color: #DD0000&quot;&gt;&#039;seconds&#039;&lt;/span&gt;&lt;span style=&quot;color: #007700&quot;&gt;;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #0000BB&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;That&#039;s it. We are done. The user can now include a countdown until a node is published or unpublished in her views. Actually it should be quite easy to use our handler in other modules for other kinds of countdowns.&lt;/p&gt;
&lt;p&gt;The final code can be found here: &lt;a href=&quot;http://drupalcode.org/project/scheduler.git/blob_plain/refs/heads/6.x-1.x:/scheduler.views.inc&quot;&gt;scheduler.views.inc&lt;/a&gt; and &lt;a href=&quot;http://drupalcode.org/project/scheduler.git/blob_plain/refs/heads/6.x-1.x:/scheduler_handler_field_scheduler_countdown.inc&quot;&gt;scheduler_handler_field_scheduler_countdown.inc&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Sun, 09 Jan 2011 15:32:46 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">5 at http://www.ericschaefer.org</guid>
 <comments>http://www.ericschaefer.org/blog/2011/01/09/custom-field-handlers-for-views-2-drupal#comments</comments>
</item>
<item>
 <title>Does TDD lead to inefficient algorithms?</title>
 <link>http://www.ericschaefer.org/blog/2009/11/02/does-tdd-lead-to-inefficient-algorithms</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Bob Martin showed in a recent blog post &quot;&lt;a href=&quot;http://blog.objectmentor.com/articles/2009/10/06/echoes-from-the-stone-age&quot;&gt;Echoes from the stone age&lt;/a&gt;&quot; how TDDing a sorting algorithm would lead to good ol&#039; bubble sort. That stirred up a heated discussion if TDD is really worth it, if it leads to &quot;inefficient&quot; algorithms.&lt;/p&gt;
&lt;p&gt;Nobody ever claimed that TDD is supposed to find algorithms that are optimized. TDD at function level defines the behaviour of functions. The behaviour of a sorting algorithm is that an initially unsorted list is sorted after the algorithm has been applied. Well, that&#039;s exactly what bubble sort does.&lt;/p&gt;
&lt;p&gt;What are you optimizing for anyway? Speed? Memory consumption? Those are behaviour too (temporal and spatial), but they were not specified in Bobs requirements (a.k.a. tests). If you need a sorting algorithm that sorts a certain number of elements with a certain initial order in a certain time, you can specify that in a test (some testing frameworks support performance test, e.g. &lt;a href=&quot;http://aut.tigris.org&quot;&gt;AUT&lt;/a&gt;). Bubble sort might fail that test. So what? That&#039;s the reason why we write tests first. We want to see it fail so we can fix it.&lt;/p&gt;
&lt;p&gt;Because we have all the nice tests in place that test the external, non-temporal behaviour we can go and read about sorting algorithms and eventually throw out all that bubble sort code in favour of quick sort. Our tests will find out if we made a mistake. We will know if we break something and our latest test tells us if the chosen algorithm is fast enough for our requirements.&lt;/p&gt;
&lt;p&gt;The test might even tell us that bubble sort is good enough. Why waste time on a super efficient algorithm if you need to sort a small number of items that are usually in order, but sometimes a new element is added to the list out of order. Bubble sort does that in one pass. You wont need quick sort, if the average number of items is 10.&lt;/p&gt;
&lt;p&gt;Oh, by the way: You don&#039;t want to be caught committing premature optimization anyway, do you?&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Mon, 02 Nov 2009 19:01:07 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">4 at http://www.ericschaefer.org</guid>
 <comments>http://www.ericschaefer.org/blog/2009/11/02/does-tdd-lead-to-inefficient-algorithms#comments</comments>
</item>
<item>
 <title>Implicit Type Parameter for Implementing Type</title>
 <link>http://www.ericschaefer.org/blog/2009/04/24/implicit-type-parameter-for-implementing-type</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;A while ago I had an interesting twitter discussion with &lt;a href=&quot;http://turbulentintellect.blogspot.com/&quot;&gt;Chris Ammerman&lt;/a&gt; (&lt;a href=&quot;http://twitter.com/cammerman&quot;&gt;@cammerman&lt;/a&gt;) about type parameters of generic interfaces (C#). Chris &lt;a href=&quot;http://twitter.com/cammerman/status/1259884390&quot;&gt;tweeted&lt;/a&gt; &quot;I often encounter a situation where I want a generic interface, but the type param should ALWAYS be the type of the implementing class&quot;. One example of this is &lt;code&gt;ICloneable&lt;/code&gt;. Would it ever be acceptable for &lt;code&gt;ICloneable.Clone()&lt;/code&gt; to return an object of a type other than the implementing type of the interface? With &lt;code&gt;ICloneable&lt;/code&gt; you will always want to cast the result of &lt;code&gt;Clone()&lt;/code&gt; because most of the time you want &lt;em&gt;The Real Thing&lt;/em&gt;® instead of &lt;code&gt;System.Object&lt;/code&gt;. For that reason many people create their own generic interface:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;public interface ICloneable&amp;lt;T&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    T Clone();&lt;br /&gt;
}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;public class Foo : ICloneable&amp;lt;Foo&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    Foo Clone() { /* implement me */ }&lt;br /&gt;
}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is still ugly. I want this instead:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;public interface ICloneable&lt;br /&gt;
{&lt;br /&gt;
    implementer Clone();&lt;br /&gt;
}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;public class Foo : ICloneable&lt;br /&gt;
{&lt;br /&gt;
    Foo Clone() { /* implement me */ }&lt;br /&gt;
}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;implementer&lt;/code&gt; would be a keyword like &lt;code&gt;base&lt;/code&gt; or &lt;code&gt;this&lt;/code&gt; but it would not refer to an object but to a type. It would behave as an implicit type parameter to the interface. This would not only be convenient syntactic sugar. It would also make the use of the parameter explicit. You couldn&#039;t do &lt;code&gt;class Foo : ICloneable&amp;lt;Bar&amp;gt;&lt;/code&gt; anymore.&lt;/p&gt;
&lt;p&gt;List of Tweets:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1259884390&quot;&gt;http://twitter.com/cammerman/status/1259884390&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1259885347&quot;&gt;http://twitter.com/cammerman/status/1259885347&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/Eric_Schaefer/status/1259909605&quot;&gt;http://twitter.com/Eric_Schaefer/status/1259909605&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1259954878&quot;&gt;http://twitter.com/cammerman/status/1259954878&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/Eric_Schaefer/status/1259983824&quot;&gt;http://twitter.com/Eric_Schaefer/status/1259983824&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1259987966&quot;&gt;http://twitter.com/cammerman/status/1259987966&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/Eric_Schaefer/status/1259996847&quot;&gt;http://twitter.com/Eric_Schaefer/status/1259996847&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1260008510&quot;&gt;http://twitter.com/cammerman/status/1260008510&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/Eric_Schaefer/status/1260012228&quot;&gt;http://twitter.com/Eric_Schaefer/status/1260012228&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1260018136&quot;&gt;http://twitter.com/cammerman/status/1260018136&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/Eric_Schaefer/status/1260023769&quot;&gt;http://twitter.com/Eric_Schaefer/status/1260023769&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/cammerman/status/1260028370&quot;&gt;http://twitter.com/cammerman/status/1260028370&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://twitter.com/Eric_Schaefer/status/1260040077&quot;&gt;http://twitter.com/Eric_Schaefer/status/1260040077&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
 <pubDate>Fri, 24 Apr 2009 08:00:00 +0000</pubDate>
 <dc:creator>Eric Schäfer</dc:creator>
 <guid isPermaLink="false">2 at http://www.ericschaefer.org</guid>
 <comments>http://www.ericschaefer.org/blog/2009/04/24/implicit-type-parameter-for-implementing-type#comments</comments>
</item>
</channel>
</rss>
