News

Welcome to End Point’s blog

Ongoing observations by End Point people

Surviving the Framework Hype Cycle (MWRC 2016)

Back in March, I attended MountainWest RubyConf with my co-workers Phunk and Phin. Shortly thereafter, Phin wrote about a couple of his favorite talks: Writing a Test Framework from Scratch and How to Build a Skyscraper.

I've found that another talk from the conference has stuck with me and I've referred to it several times in conversations. It is "Surviving the Framework Hype Cycle" by Brandon Hays. It is funny, engaging, insightful, and especially cathartic given the rapid pace of change in frameworks these days.

It can be tough to set aside the time to focus on a conference talk video, but I think this one is well worth it for programmers!

The 5 phases of the framework hype cycle that he identifies and elaborates on are:

  1. Technology Trigger
  2. Peak of Inflated Expectations
  3. Trough of Disillusionment
  4. Slope of Enlightenment
  5. Plateau of Productivity

And he shows how different groups of people with different needs can benefit at different stages, akin to settling new land:

  • Pioneers
  • Settlers
  • Town Planners

The ebb and flow of technology fads can be a lot easier to navigate when we realize there isn't an absolute right and wrong to technology choices, and we must consider the project and its maturity level, the people involved, and the current state of the technologies under consideration.

See also the Surviving the Framework Hype Cycle video page and Brandon's @tehviking Twitter account.

Ruby Fight Club

DSC_0002
Photo by Peter Gordon

First Rule: Do Not Talk About Ruby Fight Club

This post may get me kicked out for talking about the club, but…I was asked to share a few thoughts about something we tried out earlier this year at End Point. We ran an internal meetup dubbed Ruby Fight Club to read and discuss Practical Object-Oriented Design in Ruby (POODR) by Sandi Metz.

Poodr cover In the past we've met together as a company and in smaller team or project focused groups but this was first time we used a book as the guiding, recurring topic with a smaller, committed group who made time for it. Attendance was optional and we did have some folks who opted out even though they loved the subject matter and the idea, because they knew they couldn't make it most of the time. I think this made the group tighter-knit.

Our group of six remote engineers met weekly for one hour to discuss one chapter from the book. We worked through each chapter together which often led to Q & A sessions and deeper discussions about Ruby and CS theory in general. Each week, one member of the team would lead the discussion. It required some preparation in advance and gave us all a chance to work on our presenting and moderating skills. We used Google Hangouts to meet and the presenter would share their screen to show slides and example code.

Honing Our Craft

As consultants we have to balance technical perfection with other considerations — most often budget and timelines/deadlines. This reality does not lend itself well to developing and learning to apply new technical concepts. It's also challenging to apply these new concepts in large, established projects where our role is to maintain and extend existing code. The conventions and architecture are well established and it's hard to push against projects with inertia like this. Setting some time aside each week not tied to client projects gave us the opportunity to focus on our skills and develop them together. Although several in the group had read the POODR book before, they reported that working through each chapter and discussing it in a group setting greatly increased their understanding of the material.

Face Time

Our company has a large number of full-time remote engineers. At time of this writing close to 90% of us work in a location other than our head office in NYC. Remote work allows us the freedom to live where we want (I'm writing this from my home office in a small town in the interior of British Columbia, Canada) but requires extra effort for us to build team camaraderie and social connections. Ruby Fight Club was great for this. Although Hangouts, Skype and other web-conferencing apps have their challenges, everyone let us know they appreciated being able to spend time with the group each week. We'd often chat with each other for a few minutes before and after our book discussions.

Discussing the book (somewhat) face-to-face over video also increased the fidelity and efficiency of our discussions. When you can see people's faces, expressions and gestures it's much easier to glean information and understand them. It feels silly and obvious to say this after the fact but it's novel when the majority of your workday communication is done over IRC/Slack, email and phone/audio.

Got Meetups?

During one of our meetings I asked the group if anyone went to meetups local to them. I was surprised to learn that none in the group did so. For every person the response was similar: “I would but there aren't any groups meeting nearby”. I had assumed that I was the odd one out in this way because I live in a small town. The leader of our group lives in Chicago and I thought for sure there would be a meetup for him to attend but there wasn't. The closest group to him required a long trip across town on public transit. Occasionally I do travel 45-60 mins (each way!) from home to attend meetups in larger, nearby towns. People are often surprised I travel so far but it's worth it to me.

What Worked Well

We stuck to the time limit (mostly) which was a good thing. We found we usually had enough time to socialize a bit, discuss the book, have a Q & A time and then get back to our client work. Sharing the presenting / discussion leading role was also nice. Leading a session took a little time to prepare but because we all took turns doing this we shared the load. I'd like to personally thank Phunk (our fearless Ruby Fight Club leader) for having the idea and executing so well on it. We all really appreciate it and look forward to running more meetups like it in future.

What We Might Do Differently Next Time

I think it would be good to allow a little more time for socializing and chat. This happened naturally in Ruby Fight Club but it would be cool to devote a block of time specifically to it. It would also be nice to allow time for people to do a show and tell / lightning talk about something they are interested in. That said, having a book to work through was really great. Every week there is something to discuss and it was often a starting point to further discussions. What are you doing at your companies to stay sharp and connected? Let us know, we'd love to hear from you!

Reach customers and drive sales with MailChimp

It's a good idea for ecommerce stores to regularly contact their customers. This not only reminds customers that your business exists, but also allows the sharing of new products and resources that can enrich the lives of your customers and clients. One of the easiest ways to stay in touch is by using an email newsletter service, such as MailChimp.

MailChimp offers the regular suite of email newsletter services: lists, campaigns, and reports — but in addition, they allow an ecommerce store to integrate sales data back into MailChimp. When you have detailed shopping statistics for each subscriber, it opens new possibilities for customized marketing campaigns.

Endless possibilities

For example, imagine you have an email mailing list with 1,000 recipients. Instead of mailing the same generic newsletter to each subscriber, what if you could segment the list to identify your 100 best customers, and email them a special campaign?

Additional ideas could include:

  • Reach out to inactive subscribers, offering a coupon
  • Invite your best customers to a secret sale
  • Re-engage customers who placed items in their cart, but left without purchasing
  • Offer complementary products to purchasers of Product X

Automatic marketing

Once your store has sales data for subscribers, and you've decided on the campaigns you want to run with this data, the next step is to automate the process. This is where MailChimp's Automation feature comes in. Spend some time up-front to craft the automated campaigns, then sit back and let MailChimp run them for you; day in, day out.

Steps to implement

There are several off-the-shelf integrations for ecommerce stores, including Magento and BigCommerce.

Users of Perl and Interchange can use our newly-released toolsets of the Mail::Chimp3 CPAN module and the integration for Interchange5.

Contact us today for expert help integrating one of these solutions with your ecommerce store.

Go beyond the simple newsletter

Most businesses already have an email newsletter. Hopefully, you are sending regular email campaigns with it. This is a great first step. Going beyond this to segment your email list and reach out to these segments with relevant information to each of them, is the next step. Not only can this increase your sales, but it also respects your clients' and customers' time and preferences. It's a win-win for all.

Additional resource: MailChimp for Online Sellers

Create categories in Virtuemart 3 with Joomla 2.5 and 3.5 programmatically

Introduction

Code that I'm going to show you is ready to download here: https://github.com/peter-hank/com_morevirtuemart

Virtuemart is an open-source e-commerce application written in PHP. It's pretty popular with a 4% share of the whole e-commerce market (https://blog.aheadworks.com/2015/05/ecommerce-platforms-popularity-may-2015-two-platforms-take-half/). Today I will show you how to extend it and use its functionality from an external Joomla (free and open-source content management system) component.

Creating a component

We are going to create a new component to show the code in a nice and clear form. I'm using a component generator from here: https://www.component-creator.com. We don't need any tables, models and views for the purpose of this blog post. Plain and simple component is what we need. After creating the component download it and install in a Joomla administration interface.

Component overview

The component structure looks like this:

# tree components/com_morevirtuemart
components/com_morevirtuemart
├── controller.php
├── controllers
│   └── index.html
├── helpers
│   ├── index.html
│   └── morevirtuemart.php
├── index.html
├── models
│   ├── fields
│   │   ├── createdby.php
│   │   ├── filemultiple.php
│   │   ├── foreignkey.php
│   │   ├── index.html
│   │   ├── modifiedby.php
│   │   ├── submit.php
│   │   ├── timecreated.php
│   │   └── timeupdated.php
│   ├── forms
│   │   └── index.html
│   └── index.html
├── morevirtuemart.php
├── router.php
└── views
    └── index.html

We don't need to use more than a generic controller in a main directory. Its content is not very exciting for now:

<?php

// No direct access
defined('_JEXEC') or die;

jimport('joomla.application.component.controller');

/**
 * Class MorevirtuemartController
 *
 * @since  1.6
 */
class MorevirtuemartController extends JControllerLegacy
{
 /**
  * Method to display a view.
  *
  * @param   boolean $cachable  If true, the view output will be cached
  * @param   mixed   $urlparams An array of safe url parameters and their variable types, for valid values see {@link JFilterInput::clean()}.
  *
  * @return  JController   This object to support chaining.
  *
  * @since    1.5
  */
 public function display($cachable = false, $urlparams = false)
 {
  $view = JFactory::getApplication()->input->getCmd('view', '');
  JFactory::getApplication()->input->set('view', $view);

  parent::display($cachable, $urlparams);

  return $this;
 }
}

We can remove a display function and replace it with a function called createCategory so a MorevirtuemartController class will look like this:

class MorevirtuemartController extends JControllerLegacy
{
 public function createCategory ()
 {
  echo 'I\'m alive!';

  die();
 }
}

To test that our component is fully working right now try to open Joomla with this url: index.php?option=com_morevirtuemart&task=createCategory. Of course you need to prepend it with your domain name to make it work. The result should be just an empty page with our text: "I'm alive!"

Now you know how to call a controller task within a browser.

Creating a category

To use Virtuemart classes we need to initialize all of its logic and configuration. Look here:

<?php

// No direct access
defined('_JEXEC') or die;

jimport('joomla.application.component.controller');

// loading Virtuemart classes and configuration
if (!class_exists( 'VmConfig' )) require(JPATH_ADMINISTRATOR . DS . 'components' . DS . 'com_virtuemart'.DS.'helpers'.DS.'config.php');
$config= VmConfig::loadConfig();
if (!class_exists( 'VirtueMartModelVendor' )) require(JPATH_VM_ADMINISTRATOR.DS.'models'.DS.'vendor.php');
if(!class_exists('TableMedias')) require(JPATH_VM_ADMINISTRATOR.DS.'tables'.DS.'medias.php');
if(!class_exists('TableCategories')) require(JPATH_VM_ADMINISTRATOR.DS.'tables'.DS.'categories.php');
if (!class_exists( 'VirtueMartModelCategory' )) require(JPATH_VM_ADMINISTRATOR.DS.'models'.DS.'category.php');

/**
 * Class MorevirtuemartController
 *
 * @since  1.6
 */
class MorevirtuemartController extends JControllerLegacy
{
 public function createCategory ()
 {
  echo 'I\'m alive!';
  die();
 }
}

What we are doing here is importing needed Virtuemart classes and initializing the configuration of each. We are all set now and ready for an implementation.

The most difficult thing here is how to work with a Virtuemart internal authorization system. Before an every CRUD action there is a check for an authorization access. We need to find a workaround for this, there are multiple way of doing this, I will show you how I did that. Create a new file in components/com_morevirtuemart/model and call it category.php. Inside this file put this piece of code:

<?php

// No direct access
defined('_JEXEC') or die;

class VirtueMartModelCategoryLocal extends VirtueMartModelCategory {
 public function __construct() {
  parent::__construct();
 }
}

I hope it's pretty clear what we are doing here: extending a Virtuemart category model. What I'm going to do now is to copy a store function from a VirtueMartModelCategory class and put it in our local class of a category model and remove an authentication check from it (you need to secure it by yourself but using a different method). The result of this action is:

<?php

// No direct access
defined('_JEXEC') or die;

class VirtueMartModelCategoryLocal extends VirtueMartModelCategory {
  public function __construct() 
  {
    parent::__construct();
  }

  public function store(&$data) 
  {
    $table = $this->getTable('categories');

    if ( !array_key_exists ('category_template' , $data ) ){
      $data['category_template'] = $data['category_layout'] = $data['category_product_layout'] = 0 ;
    }
    if(VmConfig::get('categorytemplate') == $data['category_template'] ){
      $data['category_template'] = 0;
    }

    if(VmConfig::get('categorylayout') == $data['category_layout']){
      $data['category_layout'] = 0;
    }

    if(VmConfig::get('productlayout') == $data['category_product_layout']){
      $data['category_product_layout'] = 0;
    }

    $table->bindChecknStore($data);

    if(!empty($data['virtuemart_category_id'])){
      $xdata['category_child_id'] = (int)$data['virtuemart_category_id'];
      $xdata['category_parent_id'] = empty($data['category_parent_id'])? 0:(int)$data['category_parent_id'];
      $xdata['ordering'] = empty($data['ordering'])? 0: (int)$data['ordering'];

        $table = $this->getTable('category_categories');

      $table->bindChecknStore($xdata);

    }

    $this->clearCategoryRelatedCaches();

    return $data['virtuemart_category_id'] ;
  }
}

In addition, I removed call to media creation. If you need to create media along with the categories you need to extend it as well, the same as we did with a category model. We are all set now to create a new category from a component controller, we just need to include an extended model there and call a store function. Our controller should look like this now:

categoryModel = new VirtueMartModelCategoryLocal();
 }

 public function createCategory ()
 {
  $catData = [
   'category_name' => 'Brand new Virtuemart category',
  'category_parent_id' => 0,
   'published' => 0
   ];

  $catId = $this->categoryModel->store($catData);

  echo 'Created a new category';

  die();
 }
}

Wow! The new category should be there. There are many more available attributes to be set, we've used a needed minimum: category name, its parent and status.

The end

There are a few things I should mention:

  • ensure to add an authentication to your code, you can use a Joomla authentication system,
  • be aware that Virtuemart code can change in time so be careful with updates,
  • I recommend to use a CLI script for this kind of operations.
Thanks for reading.

Creating a recent activity feed for Thredded

Introduction

Thredded is open source forum/message board software for Rails 4.2+. The project is still alive and maintained by its author actively. For a new Rails project for a client in End Point we used Thredded as a part of an application stack. It works pretty well, but sometimes it lacks some important features. Fortunately, it's coded very nice and easy to extend its core functionality. This time we wanted to create a recent activity feed for a current user. I wanted to share this with you because I think that it's a popular widget in many social community sites.

Plan

We decided to put two main data sources for the feed:

  • user personal message notifications,
  • user forum message notifications (you may want to put some extra features here to show posts only from topics where the user is directly involved in by writing a message, for our needs it was enough to show every new post that the user was able to see).

Getting data

Let's say that we created a WidgetController controller. How to get data that we are interested in? It's not so difficult. First, this is how we are getting the latest 5 private messages for a user:

private_messages = Thredded::PrivateTopic
  .distinct
  .for_user(current_user)
  .order_recently_updated_first
  .includes(:user)
  .limit(5)
We use a PrivateTopic model to get private message for a current_user, sorted by date in descending order, including a message author and limiting to 5 records.

Now, to get a regular topic posts we need to:

posts = current_user
  .thredded_posts
  .where(messageboard_id: Pundit.policy_scope(current_user, Thredded::Messageboard.all).pluck(:id))
  .order_newest_first
  .limit(5)

We get the latest 5 posts from all the threads that a user has access to. Assuming that an action name is show, this is how it can looks all together (widget_controller.rb):

class WidgetController < ApplicationController
  def show 
    @messages = []

    posts = current_user
      .thredded_posts
      .where(messageboard_id: Pundit.policy_scope(current_user, Thredded::Messageboard.all).pluck(:id))
      .order_newest_first
      .limit(5)
    posts.each do |post|
      topic = Thredded::Topic.find(post.postable_id)

      @messages << {
        title: topic.title,
        path: Thredded::UrlsHelper::topic_url(topic, only_path: true),
        author: User.find(post.user_id),
        type: 'forum',
        created_at: post.created_at
      }
    end  

    private_messages = Thredded::PrivateTopic
      .distinct
      .for_user(current_user)
      .order_recently_updated_first
      .includes(:last_user, :user)
      .limit(5)

    private_messages.each do |pm|
      @messages << {
        title: pm.title,
        path: Thredded::UrlsHelper::topic_url(pm, only_path: true),
        author: User.find(pm.user_id),
        type: 'pm',
        created_at: pm.created_at
      }
    end   

    @messages = @messages.sort_by{|e| e[:created_at]}.reverse.take(5)
  end  
end
We merged two lists of items, sorted by date and limited to show only 5 items and created a view variable to access it from a view that we are going to create now.

Showing data

The data is ready now, the only thing that we need to do now is to create a view. We are going to use haml as a templating language. Create a file called show.html.haml. Put this piece of code in there:

%table
  %tbody
    - @messages.each do |message|
      %tr
        %td
          - if message[:type] == 'pm'
            %strong= link_to truncate('Private message', :length => 25), message[:path]
            from
            = ' '
            = link_to message[:author].username, user_path(message[:author])
            = ' '
            = time_ago_in_words(message[:created_at])
          - else
            %strong= link_to truncate(message[:title], :length => 25), message[:path]
            by
            = ' '
            = link_to message[:author].username, user_path(message[:author])
            = ' '
            = time_ago_in_words(message[:created_at])

We are looping through a list of messages. We need to distinguish between a private message and a regular post, just to make it nicer and user friendly. You may need to play a little bit with an author's property, it may be different for your project. If you have any problems, please comment! Good luck!

The end

The only documentation I know about is here: https://github.com/thredded/thredded

Thanks for reading.