<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/piggybak/</id>
  <link href="https://www.endpointdev.com/blog/tags/piggybak/"/>
  <link href="https://www.endpointdev.com/blog/tags/piggybak/" rel="self"/>
  <updated>2016-02-19T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Grammy Awards at Musica Russica</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/02/grammy-awards-at-musica-russica/"/>
      <id>https://www.endpointdev.com/blog/2016/02/grammy-awards-at-musica-russica/</id>
      <published>2016-02-19T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I was excited to learn this week about a Grammy Award recently awarded in connection with one of End Point’s valued customers, Musica Russica. &lt;a href=&#34;http://www.musicarussica.com/&#34;&gt;Musica Russica&lt;/a&gt; is a specialized e-commerce website selling Russian choral sheet music as well as choral music recordings.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2016/02/grammy-awards-at-musica-russica/image-0.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/02/grammy-awards-at-musica-russica/image-0.jpeg&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Last week’s Grammy-Award-winning (Best Choral Performance) recording by the Kansas City/Phoenix Chorales, conducted by Charles Bruffy, is featured at Musica Russica. Take a moment and listen to samples of &lt;a href=&#34;https://web.archive.org/web/20160312111456/http://www.musicarussica.com/compact_discs/a136&#34;&gt;Rachmaninoff&amp;rsquo;s All-Night Vigil&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is the third Grammy award for best choral recordings won with music published by Musica Russica, adding to their awards from 2007 and 2015.&lt;/p&gt;
&lt;p&gt;Musica Russica is the largest publisher of Russian choral music outside of Russia. Musica Russica’s business is built on a large collection of choral music which the founder, Dr. Vladimir Morosan, collected in his home country. Vladimir was passionate about this historical music and was concerned that it would not be properly preserved. In the 1980s he collected sheet music from libraries and churches in the former Soviet Union bringing the collection to the United States.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2016/02/grammy-awards-at-musica-russica/image-1.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/02/grammy-awards-at-musica-russica/image-1.jpeg&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not only did Vladimir collect music and develop a business to distribute it to music lovers around the world, he has also spent a career directing choirs to sing the music he loves. Vladimir is the founder and director of the &lt;a href=&#34;http://www.archangelvoices.com/&#34;&gt;Archangel Voices&lt;/a&gt; ensemble. On the Musica Russica website, you can purchase CDs or MP3s of recorded music. For much of the music, you can find the underlying sheet music on the website as well.&lt;/p&gt;
&lt;p&gt;End Point has worked with Musica Russica since 2012 when we built their custom e-commerce website with the Ruby on Rails framework. We developed an easy to use shopping cart and one page checkout for them as an open source project called &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;End Point congratulates everyone at Musica Russica and all those involved with the Rachmaninoff&amp;rsquo;s All-Night Vigil recording.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak: Recent Updates and Upgrades</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/09/piggybak-recent-updates-and-upgrades/"/>
      <id>https://www.endpointdev.com/blog/2014/09/piggybak-recent-updates-and-upgrades/</id>
      <published>2014-09-17T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, an open source Ruby on Rails ecommerce gem, implemented as a mountable solution, has continued to be upgraded and maintained over the last several months to keep up to date with Rails security releases and Ruby releases. Here are some quick notes on recent work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Piggybak (version 0.7.5) is now compatible with Rails 4.1.6, which is the most up to date release of Rails. See &lt;a href=&#34;http://weblog.rubyonrails.org/2014/9/12/Rails-4-1-6-and-4-0-10-has-been-released/&#34;&gt;the Rails release notes&lt;/a&gt; for more details on this recent release. The &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak Demo&lt;/a&gt; is now running on Rails 4.1.6.&lt;/li&gt;
&lt;li&gt;Piggybak is compatible with Ruby 2.1.2, and the demo is running on Ruby 2.1.2.&lt;/li&gt;
&lt;li&gt;Recent updates in &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt; include migration fixes to handle table namespace issues, and updates to remove methods that are no longer present in Rails (that were previously deprecated).&lt;/li&gt;
&lt;li&gt;Recent updates to &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;the demo&lt;/a&gt; include updates to the integration testing suite to allow testing to be compatible with Rails 4.1.6, as well as modifications to how the demo handles exceptions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure to check out &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak on github&lt;/a&gt; repository for more details on these recent updates.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak: Upgrade to Rails 4.1.0</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/04/piggybak-upgrade-to-rails-410/"/>
      <id>https://www.endpointdev.com/blog/2014/04/piggybak-upgrade-to-rails-410/</id>
      <published>2014-04-16T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt; and gems available in the demo (piggybak_variants, piggybak_giftcerts, piggybak_coupons, piggybak_bundle_discounts, piggybak_taxonomy) have been updated to Rails 4.1.0, Ruby 2.1.1 via Piggybak version gem 0.7.1. Interested in the technical details of the upgrade? Here are some fine points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dependencies were refactored so that the parent Rails app controls the Rails dependency only. There was a bit of redundancy in the various plugin gemspec dependencies. This has been cleaned up so the parent Rails app shall be the canonical reference to the Rails version used in the application.&lt;/li&gt;
&lt;li&gt;Modified use of assets which require “//= require piggybak/piggybak-application” to be added to the assets moving forward. There have been several observed issues with precompling and asset behavior, so I simplified this by requiring this require to be added to the main Rails application.js for now. The engine file is supposed to have a way around this, but it has not behaved as expected, specifically on unique deployment architectures (e.g. Heroku). Patches welcome to address this.&lt;/li&gt;
&lt;li&gt;Tables migrated to namespaced tables, e.g. “orders” migrated to “piggybak_orders”. This is how namespaced engine tables are supposed to look, and this upgrade fixes the table names with a migration and related code.&lt;/li&gt;
&lt;li&gt;Handled strong parameters. This was one of the most significant jumps from Rails 3 to Rails 4. The main element of Piggybak that needed updating here was the orders controller, which receives the order parameters and must determine which parameters to handle. Any references to attr_accessible in the code were removed.&lt;/li&gt;
&lt;li&gt;ActiveRecord “find” method replaced with where &amp;amp; chaining, where applicable. The jump to Rails 4.0 deprecated find methods, but did not remove support, but the jump to Rails 4.1 removed support of these finder methods. These were removed.&lt;/li&gt;
&lt;li&gt;Scope syntax update. Rails 4 handles scopes with new syntax, and all default scope and named scopes were updated to reflect this new syntax.&lt;/li&gt;
&lt;li&gt;Validates syntax updated. Rails 4 has new validates syntax which accepts arguments, e.g. presence: true, uniqueness: true. Piggybak was upgraded to use the new syntax, although the old syntax is still supported.&lt;/li&gt;
&lt;li&gt;Significant routes update. Rails 4 introduced a significant change in routing, and Piggybak was updated to reflect these changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full commits of Piggybak are available for browsing &lt;a href=&#34;https://github.com/piggybak/piggybak/commit/ef4a33ba199c27e18e39434c7cd9aec659c2081f&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;https://github.com/piggybak/piggybak/commit/9174688a0f96cedb1b8707b54898a8b5fdbb9393&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;wishlist&#34;&gt;Wishlist&lt;/h3&gt;
&lt;p&gt;There are a few things that I’d love to see adopted in Piggybak, with the help of the community. These include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider move to &lt;a href=&#34;http://coffeescript.org/&#34;&gt;CoffeeScript&lt;/a&gt;. I’m still on the fence about this, but I’m seeing more projects with node and CoffeeScript lately, so I wonder if it would be worth the overhead to move to CoffeeScript.&lt;/li&gt;
&lt;li&gt;Add test coverage. Perhaps &lt;a href=&#34;https://travis-ci.org/&#34;&gt;Travis CI&lt;/a&gt; integration would make sense since it hooks into github nicely?&lt;/li&gt;
&lt;li&gt;Build out more features. Things like reviews &amp;amp; ratings, saved cart, wishlist support, and saved address support have been on the feature list for a while. It’d be nice to see movement here.&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak Dependency &amp; Demo Updates</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/11/piggybak-dependency-demo-updates/"/>
      <id>https://www.endpointdev.com/blog/2013/11/piggybak-dependency-demo-updates/</id>
      <published>2013-11-21T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Things have been quiet on the Piggybak front lately, but we recently upgraded the demo to Ruby 2.0.0 via rbenv, Rails 3.2.15, and Postgres 9.3. The &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt; runs on Debian 7 with nginx and Unicorn. The upgrade went fairly smoothly, with the exception of jQuery related issues, described below.&lt;/p&gt;
&lt;p&gt;As of jQuery 1.7, the &lt;a href=&#34;http://api.jquery.com/live/&#34;&gt;live()&lt;/a&gt; method is deprecated, replaced with the &lt;a href=&#34;http://api.jquery.com/on/&#34;&gt;on()&lt;/a&gt; method. As of jQuery 1.10.&lt;em&gt;, the live() method no longer exists. The previous version of Rails that was used on the demo, Rails 3.2.12, required the jquery-rails gem version which included an older version of jQuery. Upon upgrading to Rails 3.2.15, the attached jquery-rails gem now includes jQuery 1.10.&lt;/em&gt;, resulting in the live() method no longer existing. As a result, several of the dependencies needed to be updated to accomodate this change (&lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;Rails_admin&lt;/a&gt;, the Piggybak Coupon gem, and the Piggybak Gift Cert gem, &lt;a href=&#34;https://github.com/Codeinwp/Nivo-Slider-jQuery&#34;&gt;jQuery Nivo Slider&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;What’s next for Piggybak? Our future plans include an upgrade to support Rails 4.0. Additional features described on our last &lt;a href=&#34;/blog/2012/11/piggybak-roadmap-status-update/&#34;&gt;Roadmap Update&lt;/a&gt; include advanced taxonomy, reviews &amp;amp; ratings, saved cart, wishlist functionality, and saved address support. Piggybak continues to be a great &lt;a href=&#34;https://github.com/piggybak/piggybak#mountability&#34;&gt;mountable ecommerce solution&lt;/a&gt; for Ruby on Rails, but End Point has a great deal of experience with other popular Ruby on Rails ecommerce platforms as well.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak: End of Year Update</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/12/piggybak-end-of-year-update/"/>
      <id>https://www.endpointdev.com/blog/2012/12/piggybak-end-of-year-update/</id>
      <published>2012-12-27T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Over the last few months, my coworkers and I have shared several updates on &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt; progress (&lt;a href=&#34;/blog/2012/10/piggybak-roadmap/&#34;&gt;October 2012 Piggybak Roadmap &lt;/a&gt;, &lt;a href=&#34;/blog/2012/11/piggybak-roadmap-status-update/&#34;&gt;November 2012 Piggybak Roadmap Status Update&lt;/a&gt;).  Piggybak is an open source, mountable as a Rails Engine, Ruby on Rails ecommerce platform developed and maintained by End Point. Here&amp;rsquo;s a brief background on Piggybak followed by an end of year update with some recent Piggybak news.&lt;/p&gt;
&lt;h3 id=&#34;a-brief-background&#34;&gt;A Brief Background&lt;/h3&gt;
&lt;p&gt;Over the many years that End Point has been around, we&amp;rsquo;ve amassed a large amount of experience in working with various ecommerce frameworks, open source and proprietary. A large portion of End Point&amp;rsquo;s recent development work (we also offer database, hosting, and Liquid Galaxy support) has been with &lt;a href=&#34;http://www.icdevgroup.org/&#34;&gt;Interchange&lt;/a&gt;, a Perl-based open source ecommerce framework, and &lt;a href=&#34;http://spreecommerce.com/&#34;&gt;Spree&lt;/a&gt;, a Ruby on Rails based open sourced ecommerce framework. Things came together for Piggybak earlier this year when a new client project prompted the need for a more flexible and customizable Ruby on Rails ecommerce solution. Piggybak also leveraged earlier work that I did with light-weight Sinatra-based cart functionality.&lt;/p&gt;
&lt;p&gt;Jump ahead a few months, and now Piggybak is a strong base for an ecommerce framework with several extensions to provide advanced ecommerce features. Some of the features built and already reported on were real-time shipping lookup (USPS, UPS, and FedEx support), improvement of the Piggybak installation process, gift certificate, discount, and bundle discount support.&lt;/p&gt;
&lt;h3 id=&#34;recent-progress&#34;&gt;Recent Progress&lt;/h3&gt;
&lt;p&gt;Since the last general update, we&amp;rsquo;ve tackled a number of additional changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SSL support: The Piggybak core now supports SSL for the checkout, which leverages the lightweight Rails gem &lt;a href=&#34;https://github.com/tobmatth/rack-ssl-enforcer&#34;&gt;rack-ssl-enforcer&lt;/a&gt;. A Piggybak config variable specifying that checkout should be secure must be set to true in the main Rails application, which triggers that a specific set of pages should be secure. This configuration is not ideal to use if the main Rails application requires more complex management of secure pages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Minor bug fixes &amp;amp; cleanup: The updates below include minor refactoring and/or bug fixes to the Piggybak core:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moved order confirmation outside of controller, to minimize failure of order processing if the email confirmation fails.&lt;/li&gt;
&lt;li&gt;RailsAdmin DRY cleanup&lt;/li&gt;
&lt;li&gt;Abilities (CanCan) cleanup to require less manual coding, which simplifies the code required in the CanCan model.&lt;/li&gt;
&lt;li&gt;Breakdown of orders/submit.html.erb, which allows for easier override of checkout page elements.&lt;/li&gt;
&lt;li&gt;Tax + coupons bug fixes.&lt;/li&gt;
&lt;li&gt;RailsAdmin upgrade to updated recent versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Heroku tutorial: Piggybak support in Piggybak was described &lt;a href=&#34;/blog/2012/11/piggybak-on-heroku/&#34;&gt;in this blog article&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Advanced taxonomy or product organization: An extension for advanced product organization (e.g. categories, subcategories) was released, but we still plan to add more documentation regarding its functionality and use.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bundle discount support: Another extension for bundle discount support was released. Bundle discount offers the ability to give customer discounts when a bundle or set of products has been added to the cart. Barrett shared his experiences in creating this extension &lt;a href=&#34;/blog/2012/12/piggybak-extensions-basic-how-to-guide/&#34;&gt;in this article&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fancy&lt;/strong&gt; jQuery tour: I wrote about &lt;a href=&#34;/blog/2012/12/interactive-piggybak-demo-tour/&#34;&gt;a new Piggybak demo tour&lt;/a&gt; that I created for checking out the features of Piggybak.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Advanced product optioning: Another extension for advanced product option support (e.g. size, color) was released a couple of months ago, but &lt;a href=&#34;/blog/2012/12/advanced-product-options-variants-in/&#34;&gt;this recent article&lt;/a&gt; provides more documentation on its functionality and use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;At this point, one of our big goals is to grow the Piggybak portfolio and see many of the extensions in action. We&amp;rsquo;d also like to improve the Piggybak core and extension documentation to help get folks up and running on Piggybak quickly. In addition to documentation and portfolio growth, additional features we may focus on are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product reviews &amp;amp; ratings support&lt;/li&gt;
&lt;li&gt;Saved address/address book support&lt;/li&gt;
&lt;li&gt;Wishlist, saved cart functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few large features that are on our wishlist that may need client sponsorship for build-out are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple shipping addresses per order: This allows for users to select multiple shipping addresses per order. I implemented this functionality for &lt;a href=&#34;http://www.paper-source.com/&#34;&gt;Paper Source&lt;/a&gt; just over a year ago. This would likely be developed in the form of an extension that requires several non-trivial Piggybak core overrides.&lt;/li&gt;
&lt;li&gt;Subscription support: The &lt;a href=&#34;https://groups.google.com/forum/?fromgroups#!forum/piggybak&#34;&gt;Piggybak Google Group&lt;/a&gt; has expressed interest in subscription support, which also is not trivial.&lt;/li&gt;
&lt;li&gt;Point-based credit support&lt;/li&gt;
&lt;li&gt;Multi-store architecture: End Point is very familiar with multi-store architecture, which allows multiple stores to be support via one code base. I shared some of the options &lt;a href=&#34;/blog/2012/02/multi-store-architecture-ecommerce/&#34;&gt;in this blog article&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;One deal at a time support: This is another popular feature that End Point has been involved with for &lt;a href=&#34;http://www.backcountry.com/&#34;&gt;Backcountry.com&lt;/a&gt; sites &lt;a href=&#34;http://www.steepandcheap.com/&#34;&gt;Steep and Cheap&lt;/a&gt;, &lt;a href=&#34;http://www.whiskeymilitia.com/&#34;&gt;WhiskeyMilitia.com&lt;/a&gt;, and &lt;a href=&#34;http://www.chainlove.com/&#34;&gt;Chainlove.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;get-involved&#34;&gt;Get Involved&lt;/h3&gt;
&lt;p&gt;If you are interested in helping develop Piggybak, don&amp;rsquo;t hesitate to jump on the &lt;a href=&#34;https://groups.google.com/forum/?fromgroups#!forum/piggybak&#34;&gt;Piggybak google group&lt;/a&gt; or tackle one of the &lt;a href=&#34;https://github.com/piggybak/piggybak/issues&#34;&gt;Piggybak GitHub issues&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Advanced Product Options (Variants) in Piggybak</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/12/advanced-product-options-variants-in/"/>
      <id>https://www.endpointdev.com/blog/2012/12/advanced-product-options-variants-in/</id>
      <published>2012-12-18T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;About a month ago, Tim Case and I developed and released a &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt; extension &lt;a href=&#34;https://github.com/piggybak/piggybak_variants&#34;&gt;piggybak_variants&lt;/a&gt;, which provides advanced product optioning (or variant) support in Piggybak. Piggybak is an open source Ruby on Rails ecommerce platform developed and maintained by End Point. Here, I discuss the background and basics of the extension.&lt;/p&gt;
&lt;h3 id=&#34;motivation--background&#34;&gt;Motivation &amp;amp; Background&lt;/h3&gt;
&lt;p&gt;The motivation for this extension was the common ecommerce need for product options (e.g. size, color), where each variation shares high-level product information such as a title and description, but variants have different options, quantities available, and prices. Having been intimately familiar with Spree, another open source Ruby on Rails ecommerce framework, we decided to borrow similarities of Spree&amp;rsquo;s product optioning data model after seeing its success in flexibility over many projects. The resulting model is similar to Spree&amp;rsquo;s data model, but a bit different due to the varied nature in Piggybak&amp;rsquo;s mountability design.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-0.png&#34;/&gt;
&lt;p&gt;Spree&amp;rsquo;s data model for advanced product optioning. A product has many variants. Each variant has and belongs to many option values. A product also has many options, which define which option values can be assigned to it.&lt;/p&gt;
&lt;h3 id=&#34;piggybak-variants-data-model&#34;&gt;Piggybak Variants Data Model&lt;/h3&gt;
&lt;img border=&#34;0&#34; height=&#34;112&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-1.png&#34; width=&#34;391&#34;/&gt;
&lt;p&gt;Option configuration data model in Piggybak&lt;/p&gt;
&lt;p&gt;The data model starts with option configurations, option configurations are created and specify which class they belong to. For example, a &lt;em&gt;Shirt&lt;/em&gt; model may have options &lt;em&gt;Size&lt;/em&gt; and &lt;em&gt;Color&lt;/em&gt;, and this would be stored in the option configurations table. In this case, an option will have a name (e.g. &lt;em&gt;Size&lt;/em&gt; and &lt;em&gt;Color&lt;/em&gt;) and a position for sorting (e.g. 1 and 2). The option configuration will reference an option and assign a klass to that option (in this case &lt;em&gt;Shirt&lt;/em&gt;). Another example of option configurations may be a Picture Frame, that has option configurations for &lt;em&gt;Dimensions&lt;/em&gt; and &lt;em&gt;Finish&lt;/em&gt;.&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;124&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-2.png&#34; width=&#34;301&#34;/&gt;
&lt;p&gt;Option value configuration in Piggybak&lt;/p&gt;
&lt;p&gt;After option configurations are defined, one will define option values for each option configuration. For example, option values will include &lt;em&gt;Red&lt;/em&gt;, &lt;em&gt;Blue&lt;/em&gt;, and &lt;em&gt;Green&lt;/em&gt; for the option &lt;em&gt;Color&lt;/em&gt; with position 1, 2, and 3. And option values will include &lt;em&gt;Small&lt;/em&gt;, &lt;em&gt;Medium&lt;/em&gt;, and &lt;em&gt;Large&lt;/em&gt; with positions 1, 2, and 3 for the option &lt;strong&gt;Size&lt;/strong&gt;.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-3.png&#34; width=&#34;750&#34;/&gt;
&lt;p&gt;After options, option configurations, and option values are defined, we are ready to create our variants. Per the above data model, a variant has and belongs to many option_values_variants (and must have one value per option). In our &lt;em&gt;Shirt&lt;/em&gt; example, a variant must have one &lt;em&gt;Color&lt;/em&gt; option value and one &lt;em&gt;Size&lt;/em&gt; option value assigned to it through the option_values_variants table. A variant belongs to a specific sellable item (Shirt) through a polymorphic relationship, which is consistent with Piggybak&amp;rsquo;s mountability design to allow different classes to be sellable items. Finally, a variant has_one piggybak_sellable and accepts piggybak_sellable attributes in a nested form, which means that a variant has one sellable which contains quantity, pricing, and cart description information. What this gives us is a sellable item (Shirt) with many variants where each variant has option values and each variant has sellable information such as quantity available, price, and description in cart. Below I&amp;rsquo;ll provide a few screenshots of what this looks like in the admin and front-end interface.&lt;/p&gt;
&lt;h3 id=&#34;how-to--use-the-plugin&#34;&gt;How to  Use the Plugin&lt;/h3&gt;
&lt;p&gt;To install the extension, the following steps must be applied:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the gem to the Gemfile and run bundle install&lt;/li&gt;
&lt;li&gt;Install and run the extension rake piggybak_variants:install:migrations and rake db:migrate&lt;/li&gt;
&lt;li&gt;Add acts_as_sellable_with_variants to any model that should have variants. You may need to add appropriate attr_accessible settings in your model as well, depending on your attribute accessibility settings.&lt;/li&gt;
&lt;li&gt;In the admin, define option configurations and option values for each option, then create variants for your sellable instances.&lt;/li&gt;
&lt;li&gt;Finally, add &amp;lt;%= variant_cart_form(@instance) %&amp;gt; to your sellable item&amp;rsquo;s show page to render the cart form.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These steps are similar to Piggybak&amp;rsquo;s core behavior for adding non-variant sellable items.&lt;/p&gt;
&lt;h3 id=&#34;screenshots&#34;&gt;Screenshots&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt; uses this extension for selling several product options of photography frames. The images and captions below represent the variants extension for this use case.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-4.png&#34; style=&#34;border:5px solid #E6E6E6;margin-bottom:0px;&#34; width=&#34;700&#34;/&gt;
&lt;p&gt;The &lt;em&gt;Frame&lt;/em&gt; class has two options assigned to it (Frame Size and Frame Finish). Since Frame Size has a position equal to one and Frame Finish has a position equal to two, Frame Size will show as the first option on the product page.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-5.png&#34; style=&#34;border:5px solid #E6E6E6;margin-bottom:0px;&#34; width=&#34;700&#34;/&gt;
&lt;p&gt;The &lt;em&gt;Frame Finish&lt;/em&gt; option is assigned to the &lt;em&gt;Frame&lt;/em&gt; class and it has four option values (Black, Cherry, Bronze, and Iron).&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-6.png&#34; style=&#34;border:5px solid #E6E6E6;margin-bottom:0px;&#34; width=&#34;700&#34;/&gt;
&lt;p&gt;On the Frame edit page, 8 variants are created to represent the combinations of 2 Frame Sizes and 4 Frame Finishes.
Each variant has pricing, quantity, and cart description information, as well as additional sellable fields.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/advanced-product-options-variants-in/image-7.png&#34; style=&#34;border:5px solid #E6E6E6;margin-bottom:0px;&#34; width=&#34;700&#34;/&gt;
&lt;p&gt;And the product page shows the options and option values for that item, displayed based on Position and Size data.
When each option value is triggered, appropriate pricing information is displayed.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The goal of this extension was to provide variant functionality that is not necessarily required to be used with Piggybak. Piggybak can still be leveraged without this extension to provide simple single product option add to cart functionality. The Piggybak cart only examines what elements are in the cart based on the sellable_id and the quantity, which is the driving force of the core Piggybak architecture as well as this extension.&lt;/p&gt;
&lt;p&gt;Stay tuned for additional updates to the &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak Ruby on Rails Ecommerce&lt;/a&gt; platform.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak Extensions: A Basic How-To Guide</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/12/piggybak-extensions-basic-how-to-guide/"/>
      <id>https://www.endpointdev.com/blog/2012/12/piggybak-extensions-basic-how-to-guide/</id>
      <published>2012-12-13T00:00:00+00:00</published>
      <author>
        <name>Barrett Griffith</name>
      </author>
      <content type="html">
        &lt;p&gt;This article outlines the steps to build an extension for &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;. Piggybak is an open-source Ruby on Rails ecommerce platform created and maintained by End Point. It is developed as a Rails Engine and is intended to be mounted on an existing Rails application. If you are interested in developing an extension for Piggybak, this article will help you identify the steps you need to take to have your extension leveraging the Piggybak gem, and integrating smoothly into your app.&lt;/p&gt;
&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;The Piggybak platform is lightweight and relies on Rails meta-programming practices to integrate new extensions. The best references to use alongside your development should be the previously developed extensions found here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_bundle_discounts&#34;&gt;Bundle Discounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_coupons&#34;&gt;Coupons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_variants&#34;&gt;Variants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_taxonomy&#34;&gt;Taxonomy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_realtime_shipping&#34;&gt;Real-time Shipping&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is likely that your extension will tie into the admin interface. Piggybak utilizes the &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin gem&lt;/a&gt; for its admin interface.&lt;/p&gt;
&lt;h3 id=&#34;setting-up-the-development-environment&#34;&gt;Setting up the Development Environment&lt;/h3&gt;
&lt;p&gt;A convenient way to start building out your extension is to develop against the demo app found &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;here&lt;/a&gt;. The demo app utilizes the Piggybak gem and comes with sample data to populate the e-commerce store.&lt;/p&gt;
&lt;p&gt;The Piggybak demo app &lt;a href=&#34;https://github.com/piggybak/demo/blob/master/sample.psql&#34;&gt;sample data&lt;/a&gt; is exported for a PostgreSQL database. To use this data (suggested) you should be prepared to do one of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;be using PostgreSQL and understand how to work with the existing data dump&lt;/li&gt;
&lt;li&gt;transform this data dump to another database format that fits your database flavor of choice&lt;/li&gt;
&lt;li&gt;ignore the sample data and create your own&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;creating-the-extension-gem-engine&#34;&gt;Creating the Extension (Gem, Engine)&lt;/h3&gt;
&lt;p&gt;In a folder outside of the project utilizing the Piggybak gem, create a mountable rails engine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ rails plugin new [extension_name] --mountable&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &amp;ldquo;mountable&amp;rdquo; option makes you engine namespace-isolated.&lt;/p&gt;
&lt;p&gt;Next, update your app&amp;rsquo;s Gemfile to include the extension under development&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gem &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;piggybak_new_extension&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:path&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/the/path/to/the/extension&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run bundle install to install the extension in your application and restart your application.&lt;/p&gt;
&lt;h3 id=&#34;special-engine-configuration&#34;&gt;Special Engine Configuration&lt;/h3&gt;
&lt;p&gt;Your extension will rely on the engine.rb file to integrate with Piggybak. A sample engine.rb for the piggybak_bundle_discount can be found &lt;a href=&#34;https://github.com/piggybak/piggybak_bundle_discounts/blob/master/lib/piggybak_bundle_discounts/engine.rb&#34;&gt;here&lt;/a&gt;. Let&amp;rsquo;s go over this file to get a clue of how bundle discounts are served as an extension in Piggybak.&lt;/p&gt;
&lt;p&gt;Make sure you are requiring any of your classes at the top of your engine.rb file, e.g.:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;piggybak_bundle_discounts/order_decorator&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code below is decorating the Piggybak::Order class, which is a helpful pattern to use when you wish to enhance class capabilities across engines. In the bundle discount case, the decorator adds several active record callbacks.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.to_prepare &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.send(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:include&lt;/span&gt;, ::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PiggybakBundleDiscounts&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;OrderDecorator&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;An order is comprised of many line items, which are used to calculate the balance due. More information on the line item architecture is described &lt;a href=&#34;/blog/2012/10/piggybak-update-line-item-rearchitecture/&#34;&gt;here&lt;/a&gt;. If your extension needs to register new line item types to the order, you may use something similar to the following code to set up the information regarding this new line item type.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.before_initialize &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;.config &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |config|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config.extra_secure_paths &amp;lt;&amp;lt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/apply_bundle_discount&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config.line_item_types[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:bundle_discount&lt;/span&gt;] = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:visible&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:allow_destroy&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:fields&lt;/span&gt; =&amp;gt; [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;bundle_discount&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:class_name&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;::PiggybakBundleDiscounts::BundleDiscount&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:display_in_cart&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Bundle Discount&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sort&lt;/span&gt; =&amp;gt; config.line_item_types[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payment&lt;/span&gt;][&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sort&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config.line_item_types[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payment&lt;/span&gt;][&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sort&lt;/span&gt;] += &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Does your extension need client side support? Piggybak utilizes the asset pipeline so you will need to register your assets here to have them pre-compiled.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;initializer &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;piggybak_bundle_discounts.precompile_hook&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |app|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  app.config.assets.precompile += [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;piggybak_bundle_discounts/piggybak_bundle_discounts.js&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, since Piggybak utilizes RailsAdmin for its admin system, we need to register the models as following the RailsAdmin documentation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;initializer &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;piggybak_bundle_discounts.rails_admin_config&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |app|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RailsAdmin&lt;/span&gt;.config &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |config|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config.model &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PiggybakBundleDiscounts&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;BundleDiscount&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      navigation_label &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Extensions&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      label &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Bundle Discounts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      edit &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        field &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        field &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:multiply&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          help &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Optional&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        field &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:discount&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        field &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:active_until&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        field &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:bundle_discount_sellables&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          active &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          label &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Sellables&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          help &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Required&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config.model &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PiggybakBundleDiscounts&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;BundleDiscountSellable&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      visible &lt;span style=&#34;color:#080&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      edit &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        field &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sellable&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          label &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Sellable&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          help &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Required&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;what-else&#34;&gt;What else?&lt;/h3&gt;
&lt;p&gt;From here, extension development can follow standard Rails engine development, which allows for support of its own models, controllers, views, and additional configuration. Any database migrations inside an extension must be copied to the main Rails application to be applied.&lt;/p&gt;
&lt;p&gt;You may also need to be aware of how Piggybak integrates with CanCan to ensure that CanCan permissions on your extension models are set correctly.&lt;/p&gt;
&lt;p&gt;End Point created and maintains Piggybak project. Much of the inspiration for Piggybak comes from our expert engineers who have ecommerce experience working and contributing to platforms such as Spree, RoR-e, and Interchange. If you are interested in talking with us about your next ecommerce project, or have an ecommerce project that needs support, let us know.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interactive Piggybak Demo Tour</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/12/interactive-piggybak-demo-tour/"/>
      <id>https://www.endpointdev.com/blog/2012/12/interactive-piggybak-demo-tour/</id>
      <published>2012-12-06T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;A new interactive tour of Piggybak and the Piggybak demo has been released at piggybak.org. &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt; is an open source Ruby on Rails ecommerce framework built as a Rails 3 engine and intended to be mounted on existing Rails applications.&lt;/p&gt;
&lt;p&gt;The tour leverages &lt;a href=&#34;http://codecanyon.net/item/jtour-website-tour-engine/409593&#34;&gt;jTour&lt;/a&gt; (a jQuery plugin) and guides you through the homepage, navigation page, product page, cart and checkout pages, gift certificate page, advanced product option page, and WYSIWYG driven page. The tour also highlights several of the Piggybak plugins available and installed into the demo such as plugins that introduce advanced product navigation, advanced product optioning, and gift certificate functionality. Below are a few screenshots from the demo.&lt;/p&gt;
&lt;p&gt;An interesting side note of developing this tour is that while I found many nice jQuery-driven tour plugins available for free or at a small cost, this jQuery plugin was the only plugin offering decent multi-page tour functionality.&lt;/p&gt;
&lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&#34;center&#34; valign=&#34;top&#34;&gt;&lt;a href=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-0.png&#34; width=&#34;350&#34;/&gt;&lt;/a&gt;&lt;br/&gt;Here is the starting point of Piggybak tour.&lt;/td&gt;
&lt;td align=&#34;center&#34; valign=&#34;top&#34;&gt;&lt;a href=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-1-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-1.png&#34; width=&#34;350&#34;/&gt;&lt;/a&gt;&lt;br/&gt;The Piggybak tour adds an item to the cart during the tour.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34; valign=&#34;top&#34;&gt;&lt;a href=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-2-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-2.png&#34; width=&#34;350&#34;/&gt;&lt;/a&gt;&lt;br/&gt;The Piggybak tour highlights advanced product navigation&lt;br/&gt;in the demo.&lt;/td&gt;
&lt;td align=&#34;center&#34; valign=&#34;top&#34;&gt;&lt;a href=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-3-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/12/interactive-piggybak-demo-tour/image-3.png&#34; width=&#34;350&#34;/&gt;&lt;/a&gt;&lt;br/&gt;The Piggybak tour highlights features and functionality&lt;br/&gt;on the one-page checkout.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;You can check out the interactive tour with your own &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak on Heroku</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/11/piggybak-on-heroku/"/>
      <id>https://www.endpointdev.com/blog/2012/11/piggybak-on-heroku/</id>
      <published>2012-11-12T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Several weeks ago, we were contacted through our website with a request for Heroku support on &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;. Piggybak is an open source Ruby on Rails ecommerce platform developed and maintained by End Point. Piggybak is similar to many other Rails gems in that it can be installed from Rubygems in any Rails application, and Heroku understands this requirement from the application’s Gemfile. This is a brief tutorial for getting a Rails application up and running with Piggybak. For the purpose of this tutorial, I’ll be using the existing &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt; for deployment, instead of creating a Rails application from scratch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;a)&lt;/strong&gt; First, clone the existing &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt;. This will be your base application. On your development machine (local or other), you must run bundle install to get all the application’s dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;b)&lt;/strong&gt; Next, add config.assets.initialize_on_precompile = false to config/application.rb to allow your assets to be compiled without requiring creating a local database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;c)&lt;/strong&gt; Next, compile the assets according to &lt;a href=&#34;https://devcenter.heroku.com/articles/rails3x-asset-pipeline-cedar&#34;&gt;this Heroku article&lt;/a&gt; with the command RAILS_ENV=production bundle exec rake assets:precompile. This will generate all the application assets into the public/assets/ directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;d)&lt;/strong&gt; Next, add the assets to the repo by removing public/assets/ from .gitignore and committing all modified files. Heroku’s disk read-only limitation prohibits you from writing public/assets/ files on the fly, so this is a necessary step for Heroku deployment. It is not necessary for standard Rails deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;e)&lt;/strong&gt; Next, assuming you have a Heroku account and have installed the &lt;a href=&#34;https://toolbelt.heroku.com/debian&#34;&gt;Heroku toolbelt&lt;/a&gt;, run heroku create to create a new Heroku application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;f)&lt;/strong&gt; Next, run git push heroku master to push your application to your new Heroku application. This will push the code and install the required dependencies in Heroku.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;g)&lt;/strong&gt; Next, run heroku pg:psql, followed by \i sample.psql to load the sample data to the Heroku application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;h)&lt;/strong&gt; Finally, run heroku restart to restart your application. You can access your application through a browser by running heroku open.&lt;/p&gt;
&lt;p&gt;That should be it. From there, you can manipulate and modify the demo to experiment with Piggybak functionality. The major difference between Heroku deployment and standard deployment is that all your compiled assets must be in the repository because Heroku cannot write them out on the fly. If you plan to deploy the application elsewhere, you will have to make modifications to the repository regarding public/assets.&lt;/p&gt;
&lt;p&gt;A full set of commands for this tutorial includes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Clone and set up the demo app
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone git://github.com/piggybak/demo.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bundle install
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# add config.assets.initialize_on_precompile = false
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# to config/application.rb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Precompile assets and add to repository
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RAILS_ENV=production bundle exec rake assets:precompile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# edit .gitignore here to stop ignoring public/assets/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git commit -m &amp;#34;Heroku support commit.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Deploy to Heroku
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;heroku create
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push heroku master
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;heroku pg:psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt; \i sample.psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;heroku restart
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;heroku open&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      </content>
    </entry>
  
    <entry>
      <title>Piggybak: Roadmap Status Update</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/11/piggybak-roadmap-status-update/"/>
      <id>https://www.endpointdev.com/blog/2012/11/piggybak-roadmap-status-update/</id>
      <published>2012-11-06T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;About a month ago, I shared an outline of the current &lt;a href=&#34;/blog/2012/10/piggybak-roadmap/&#34;&gt;Piggybak Roadmap&lt;/a&gt;. Piggybak is an open-source Ruby on Rails ecommerce platform created and maintained by End Point. It is developed as a Rails Engine and is intended to be mounted on an existing Rails application. Over the last month, Tim and I have been busy at work building out features in Piggybak, and completing refactoring that opens the door for better extension and feature development. Here&amp;rsquo;s a summary of the changes that we&amp;rsquo;ve finished up.&lt;/p&gt;
&lt;h3 id=&#34;real-time-shipping-lookup&#34;&gt;Real-time Shipping Lookup&lt;/h3&gt;
&lt;p&gt;One of our Piggybak clients already had integrated USPS and UPS shipping, but we decided to extract this and combine it with FedEx, to offer &lt;a href=&#34;https://github.com/piggybak/piggybak_realtime_shipping&#34;&gt;real-time shipping lookup shipping in Piggybak&lt;/a&gt;. This extension leverages &lt;a href=&#34;https://github.com/Shopify/active_shipping&#34;&gt;Shopify&amp;rsquo;s open-source ActiveShipping&lt;/a&gt; Ruby gem. When you are ready to get your Piggybak store up and running, you can include this new extension and configure USPS, UPS, and FedEx real-time shipping lookup immediately.&lt;/p&gt;
&lt;h3 id=&#34;installer-process&#34;&gt;Installer process&lt;/h3&gt;
&lt;p&gt;Tim Case updated the installation process to be more streamlined. The previous installation process was a bit crufty and required changes to your Gemfile, routes, layouts, and precompiled assets. Tim described the installation work &lt;a href=&#34;/blog/2012/11/how-to-build-command-line-executable/&#34;&gt;in this article&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;rename-variant-to-sellable&#34;&gt;Rename Variant to Sellable&lt;/h3&gt;
&lt;p&gt;A minor but notable change that happened in the last month was the change of &amp;ldquo;variant&amp;rdquo; to &amp;ldquo;sellable&amp;rdquo;. Any model in a Rails application, now can be extended with the class method acts_as_sellable, which will allow that item to be managed as a sellable item and be a purchaseable item.&lt;/p&gt;
&lt;h3 id=&#34;variants-extension&#34;&gt;Variants Extension&lt;/h3&gt;
&lt;p&gt;Tied directly to the variant to sellable change, we developed a new extension to provide &lt;a href=&#34;https://github.com/piggybak/piggybak_variants&#34;&gt;advanced variant support in Piggybak&lt;/a&gt;. The advanced variant data model has similarities to &lt;a href=&#34;http://spreecommerce.com/&#34;&gt;Spree&lt;/a&gt;&amp;rsquo;s data model, one that we have observed as a successful feature of Spree. The basic principles are that you assign specific options to sellable items (e.g. size and color), and then you assign option values to those options (e.g. red and blue for size, large and small for color). Then, for each sellable item, you can define many variants each with a different combination of options, each with a unique sku, quantity on hand, cart description, and price. The user sees these options on the product detail page, and selects option values to add items to the cart.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/11/piggybak-roadmap-status-update/image-0.png&#34; width=&#34;600&#34;/&gt;
&lt;p&gt;Advanced production optioning support in Piggybak: In this screenshot, options for frame size and frame finish are provided.&lt;/p&gt;
&lt;p&gt;Each variant has individual pricing, quantity on hand, and a description in the cart.&lt;/p&gt;
&lt;h3 id=&#34;line-item-rearchitecture&#34;&gt;Line Item Rearchitecture&lt;/h3&gt;
&lt;p&gt;I also spent a good amount of time rearchitecting line item associations to orders, where a line item now represents all monetary items in an order (sellable, payment, tax item, shipment, etc.). This results in a more simplified order total and balance due calculation, as well as allows for extensions to introduce custom line items that are included in order calculations without order processing code changes. This significant change is described &lt;a href=&#34;/blog/2012/10/piggybak-update-line-item-rearchitecture/&#34;&gt;in this article&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;piggybak-coupons&#34;&gt;Piggybak Coupons&lt;/h3&gt;
&lt;p&gt;The line item rearchitecture work was done in tandem with development of a &lt;a href=&#34;https://github.com/piggybak/piggybak_coupons&#34;&gt;Piggybak coupon extension&lt;/a&gt;. The extension includes support for defining discount type (percent, dollar, or free shipping), discount amount (for percent and dollar), minimum cart total, expiration date, allowed number of uses.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/11/piggybak-roadmap-status-update/image-1.png&#34; width=&#34;600&#34;/&gt;
&lt;p&gt;Coupon support in Piggybak: Coupon application on the checkout happens via AJAX and&lt;/p&gt;
&lt;p&gt;is displayed in the order totals calculations shown in the screenshot.&lt;/p&gt;
&lt;h3 id=&#34;gift-certificate-support&#34;&gt;Gift Certificate Support&lt;/h3&gt;
&lt;p&gt;Finally, one of the recent extensions completed was development of a &lt;a href=&#34;https://github.com/piggybak/piggybak_giftcerts&#34;&gt;gift certificate extension&lt;/a&gt;. A gift certificate can be purchased at various increments and applied on the checkout page to an order via an AJAX call. Gift certificates may also be purchased and redeemed in the Piggybak admin.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/11/piggybak-roadmap-status-update/image-2.png&#34; width=&#34;600&#34;/&gt;
&lt;p&gt;Gift Certificate support in Piggybak: Gift certificate application on the checkout happens via AJAX and is displayed in the order totals calculations shown in the screenshot. In this case, the gift certificate covers the entire order.&lt;/p&gt;
&lt;h3 id=&#34;minor-bug-fixes-refactoring-and-feature-development&#34;&gt;Minor Bug Fixes, Refactoring and Feature Development&lt;/h3&gt;
&lt;p&gt;Several bug fixes and minor refactoring was applied during development of these features, including but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;attr_accessible updates to support Rails 3 mass assignment attributes&lt;/li&gt;
&lt;li&gt;Improved inventory management on the admin side&lt;/li&gt;
&lt;li&gt;Minor refactoring to introduce &lt;a href=&#34;/blog/2012/10/association-extensions-in-rails-for/&#34;&gt;Proxy Association extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Removal of &lt;a href=&#34;https://github.com/technicalpickles/jeweler&#34;&gt;jeweler&lt;/a&gt;, and move to standard Rails engine architecture&lt;/li&gt;
&lt;li&gt;Added functionality to support copying a billing address to shipping address in admin.&lt;/li&gt;
&lt;li&gt;Added logic to enforce one payment method be added at a time via admin.&lt;/li&gt;
&lt;li&gt;JavaScript-based validation on the checkout.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_stripe&#34;&gt;Stripe payment gateway support&lt;/a&gt; via an extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;If we take a look the roadmap list a month ago, we can cross several items off from the list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Realtime Shipping with USPS, UPS, and Fedex support&lt;/li&gt;
&lt;li&gt;Improvement of Piggybak installation process&lt;/li&gt;
&lt;li&gt;Advanced Product Optioning Support&lt;/li&gt;
&lt;li&gt;Line Item Rearchitecture to support future work on Gift Certificates, Discounts&lt;/li&gt;
&lt;li&gt;Gift Certificate, Discount Support&lt;/li&gt;
&lt;li&gt;Advanced Taxonomy&lt;/li&gt;
&lt;li&gt;Reviews &amp;amp; Ratings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few new things have recently been added to the list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add SSL support in core&lt;/li&gt;
&lt;li&gt;Create Heroku deployment tutorial&lt;/li&gt;
&lt;li&gt;Saved cart, Wishlist support&lt;/li&gt;
&lt;li&gt;Saved address support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our goal for the immediate future is to focus on development of the most common ecommerce features.&lt;/p&gt;
&lt;p&gt;All of the features described in this article are part of the &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to Build a Command Line Executable Installer with Rubygems and Thor</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/11/how-to-build-command-line-executable/"/>
      <id>https://www.endpointdev.com/blog/2012/11/how-to-build-command-line-executable/</id>
      <published>2012-11-02T00:00:00+00:00</published>
      <author>
        <name>Tim Case</name>
      </author>
      <content type="html">
        &lt;p&gt;Gems for Rails often need the user to do something more for installation than just adding the gem to a Gemfile and running bundler install.  Sometimes it&amp;rsquo;s a simple matter of copying over some migration files and sometimes it&amp;rsquo;s just setting up a config file, and most of the time these simple installation steps are best handled with a well written installation section in the README file.  When the installation process is more complex a long README might not be so enticing to the potential gem user, in a world where everyone has a finger on the back button it&amp;rsquo;s nice to be able to create an installer that allows the user to complete complex installation tasks by executing a one liner and that&amp;rsquo;s where an installer made through Gem executables and &lt;a href=&#34;https://github.com/wycats/thor&#34;&gt;Thor&lt;/a&gt; can come in handy.&lt;/p&gt;
&lt;p&gt;We wanted to make it easier for new users of Piggybak to get started and decided that an installer was the best way to do that.  Creating a binary installer that is installed by Rubygems is one of those esoteric things that may not be thought of as one of the core strengths of Rubygems and Rails but it&amp;rsquo;s a bonus to be able to do something like this without a whole lot of fuss.&lt;/p&gt;
&lt;h3 id=&#34;creating-an-installer-with-rubygems-and-thor&#34;&gt;Creating an installer with Rubygems, and Thor:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;In your Rails app, create a file in your lib directory that inherits from Thor, this file will house all of your command line actions.  Thor is already included as a part of Rails so you don&amp;rsquo;t need to add it to your Gemfile.&lt;/li&gt;
&lt;li&gt;Inside your Thor subclass, define methods which will in turn become invokable actions from the command line.  Installers usually need to copy files around and execute commands, Thor provides a library the covers the most common cases which can be added to your class by including Thor::Actions &lt;a href=&#34;http://rdoc.info/github/wycats/thor/master/Thor/Actions&#34;&gt;(A list of the included actions)&lt;/a&gt;.  Have a look at the &lt;a href=&#34;https://github.com/piggybak/piggybak/blob/master/lib/piggybak/cli.rb&#34;&gt;Piggybak installer class&lt;/a&gt;, and you&amp;rsquo;ll see that the Thor actions are not too complicated to understand.&lt;/li&gt;
&lt;li&gt;Create a bin directory in your Rails directory that will be used to start your Thor class, add a file with the name of your executable which starts your Thor class (details below)&lt;/li&gt;
&lt;li&gt;Add an &amp;ldquo;executables&amp;rdquo; entry for the file in your bin directory to your gemspec file&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;add-a-file-to-your-bin-folder-than-starts-thor&#34;&gt;Add a file to your bin folder than starts Thor&lt;/h3&gt;
&lt;p&gt;The code below shows the file located in the bin directory and it could act as a template for your own executable.  The things to note are the inclusion of the ruby shebang, and the requiring of the piggybak cli class.  Finally at the end of the file the start method is sent to the Thor class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;piggybak/cli&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rescue&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;LoadError&lt;/span&gt; =&amp;gt; e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;warn&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Could not load &amp;#34;piggybak/cli&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; -&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CLI&lt;/span&gt;.start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;add-an-entry-to-your-gemspec-for-the-executable&#34;&gt;Add an entry to your gemspec for the executable&lt;/h3&gt;
&lt;p&gt;Rubygems expects the executable to be in a directory called bin which is in the same directory as the gemspec, if you want to place the executable in a different location you&amp;rsquo;ll need to specify that in your gemspec with a &amp;ldquo;bindir&amp;rdquo; entry. (Check the &lt;a href=&#34;http://docs.rubygems.org/&#34;&gt;Rubygem docs&lt;/a&gt; for a more detailed explanation.)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spec.executables &amp;lt;&amp;lt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;piggybak&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once that&amp;rsquo;s done your gem is ready to go and can be included inside the Gemfile of a Rails app.  When the gem is installed, Rubygems will place a file in your Ruby bin directory that can be invoked via the command line.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ piggybak install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      </content>
    </entry>
  
    <entry>
      <title>Association Extensions in Rails for Piggybak</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/10/association-extensions-in-rails-for/"/>
      <id>https://www.endpointdev.com/blog/2012/10/association-extensions-in-rails-for/</id>
      <published>2012-10-31T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently had a problem with Rails named scopes while working on minor refactoring in &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, an open source Ruby on Rails ecommerce platform that End Point created and maintains. The problem was that I found that &lt;a href=&#34;http://guides.rubyonrails.org/active_record_querying.html#scopes&#34;&gt;named scopes&lt;/a&gt; were not returning uncommitted or new records. Named scopes allow you to specify ActiveRecord query conditions and can be combined with joins and includes to query associated data. For example, based on recent &lt;a href=&#34;/blog/2012/10/piggybak-update-line-item-rearchitecture/&#34;&gt;line item rearchitecture&lt;/a&gt;, I wanted order.line_items.sellables, order.line_items.taxes, order.line_items.shipments to return all line items where line_item_type was sellable, tax, or shipment, respectively. With named scopes, this might look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;LineItem&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveRecord&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sellables&lt;/span&gt;, where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_item_type&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sellable&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:taxes&lt;/span&gt;, where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_item_type&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;tax&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipments&lt;/span&gt;, where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_item_type&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payments&lt;/span&gt;, where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_item_type&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, while processing an order, any uncommited or new records would not be returned when using these named scopes. To work around this, I added the &lt;a href=&#34;http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-select&#34;&gt;Enumerable select&lt;/a&gt; method to iterate over the line items, e.g.:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Reviewing shipments in an order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order.line_items.select { |li| li.line_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipment&amp;#34;&lt;/span&gt; }.all? { |s| s.shipment.status == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipped&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Get number of new payments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order.line_items.select { |li| li.new_record? &amp;amp;&amp;amp; li.line_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment&amp;#34;&lt;/span&gt; }.size&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;association-extensions&#34;&gt;Association Extensions&lt;/h3&gt;
&lt;p&gt;I felt that the above workaround was crufty and not very readable and sent out a request to my coworkers in hopes that there was a solution for improving the readability and clarity of the code. &lt;a href=&#34;/blog/authors/kamil-ciemniewski/&#34;&gt;Kamil&lt;/a&gt; confirmed that named scopes do not return uncommitted records, and Tim Case offered an alternative solution by suggesting &lt;a href=&#34;http://guides.rubyonrails.org/association-basics.html#association-extensions&#34;&gt;association extensions&lt;/a&gt;. An association extension allows you to add new finders, creators or methods that are only used as part of the association. After some investigation, I settled on the following code to extend the line_items association:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveRecord&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_items&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;sellables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      proxy_association.proxy.select { |li| li.ilne_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sellable&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;taxes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      proxy_association.proxy.select { |li| li.ilne_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;tax&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;shipments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      proxy_association.proxy.select { |li| li.ilne_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipment&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;payments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      proxy_association.proxy.select { |li| li.ilne_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;payment&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above code allows us to call order.line_items.sellables, order.line_items.taxes, order.line_items.shipments, and order.line_items.payments, which will return all new and existing line item records. These custom finder methods are used during order preprocessing which occurs during the ActiveRecord before_save callback before an order is finalized.&lt;/p&gt;
&lt;h3 id=&#34;dynamic-creation&#34;&gt;Dynamic Creation&lt;/h3&gt;
&lt;p&gt;Of course, the Piggybak code takes this a step further because additional custom line item types can be added to the code via Piggybak extensions (e.g. coupons, gift certificates, adjustments). To address this, association extensions are created dynamically in the Piggybak engine instantiation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.class_eval &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_items&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;.config.line_item_types.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |k, v|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;# k is sellable, tax, shipment, payment, etc.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      define_method &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;k.to_s.pluralize&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_association.proxy.select { |li| li.line_item_type == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;k&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The disadvantage to association extensions versus named scopes are that association extensions are not chainable, which means you cannot add methods to the association extension. For example, a named scope may allow you to query order.line_items.sellables.price_greater_than_50 to return committed line items with a price greater than 50, but this functionality would not be possible with association extensions. This is not a limitation in the current code base, but it may become a limitation in the future.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak Update: Line Item Rearchitecture</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/10/piggybak-update-line-item-rearchitecture/"/>
      <id>https://www.endpointdev.com/blog/2012/10/piggybak-update-line-item-rearchitecture/</id>
      <published>2012-10-17T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Over the last couple of weeks, I’ve been involved in doing significant rearchitecture of &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;’s line items data model. Piggybak is an open-source mountable Ruby on Rails ecommerce solution created and maintained by End Point. A few months ago after observing a few complications with Piggybak’s order model and it’s interaction with various nested elements (product line items, shipments, payments, adjustments) and calculations, and after reviewing and discussing these complications with a couple of my expert coworkers, we decided to go in the direction of a uniform line item data model based on our success with this model for other ecommerce clients over the years (whoa, that was a long sentence!). Here, I’ll discuss some of the motiivations and an overview of the technical aspects of this rearchitecture.&lt;/p&gt;
&lt;h3 id=&#34;motivation&#34;&gt;Motivation&lt;/h3&gt;
&lt;p&gt;The biggest drivers of this change were a) to enable more simplified order total calculations based on uniform line items representing products, shipments, payments, etc. and b) to enable easier extensibility or hookability into the order architecture without requiring invasive overrides. For example, the code before for order totals may looked something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order.subtotal = sum of items + sum of adjustments + sum of credits + sum of shipments
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order.total_due = sum of items + sum of adjustments + sum of payments + sum of credits + sum of shipments&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And after the order calculation, a more simplified version of order total calculation looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;self.subtotal = sum of line item prices that aren&amp;#39;t payments
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;self.total_due = sum of all line items&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A related motivation that helped drive this change was to develop several credit-based features for Piggybak such as gift certificates, coupons and bundle discounts to grow the feature set of Piggybak. Rather than requiring complex overrides to incorporate these custom credits to orders, a consistent line item interface supports integration of additional custom line item types.&lt;/p&gt;
&lt;h3 id=&#34;data-model-changes&#34;&gt;Data Model Changes&lt;/h3&gt;
&lt;p&gt;Prior to the rearchitecture, the data model looked like this:&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/10/piggybak-update-line-item-rearchitecture/image-0.png&#34;/&gt;
&lt;p&gt;Piggybak data model prior to line item rearchitecture.&lt;/p&gt;
&lt;p&gt;Some important notes on this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Line items, payments, shipments and adjustments belong to the order. An order can have many of these elements.&lt;/li&gt;
&lt;li&gt;During order processing, all of these elements had to be processed independently without uniform consistency. An order balance due represented the sum of various charge related elements (items, shipments, tax) minus any payments or adjustments.&lt;/li&gt;
&lt;li&gt;In the case of adjustments, this was a bit tricky because an adjustment could be in the form of a negative or positive amount. This introduced complications in the order calculation process.&lt;/li&gt;
&lt;li&gt;Line items represented products only.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the rearchitecture, the data model now looks like this:&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/10/piggybak-update-line-item-rearchitecture/image-1.png&#34;/&gt;
&lt;p&gt;Piggybak data model after line item rearchitecture.&lt;/p&gt;
&lt;p&gt;Important notes on this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the core Piggybak data model, line item types represent sellable (product), payment, shipment, adjustment, and tax entries.&lt;/li&gt;
&lt;li&gt;Line items can still be related to other elements, such as payment and shipment, but the line item has uniform information such as price and description.&lt;/li&gt;
&lt;li&gt;Line item types are controlled by a Piggybak configuration variable, which allows for Piggybak extensions and the main Rails application to incorporate additional custom line item types.&lt;/li&gt;
&lt;li&gt;Because various calculation methods are applied on each line item type (e.g. payments are charged against credit card, shipping is recalculated with a shipping calculator) the line item order model is amenable to custom preprocessing and processing per line item type. This takes advantage Ruby’s respond_to? method to determine if specific preprocessing or postprocessing methods exist.&lt;/li&gt;
&lt;li&gt;The new architecture also takes advantage of metaprogramming by defining methods dynamically against the line item types. For example, order instance methods &amp;ldquo;shipping_charge&amp;rdquo; and &amp;ldquo;tax_charge&amp;rdquo; are dynamically created which return the sum of line item prices where the line item type is shipping or tax, respectively.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;coupon-support-in-piggybak&#34;&gt;Coupon Support in Piggybak&lt;/h3&gt;
&lt;p&gt;Much of the line item rearchitecture work was done in tandem with development of a Piggybak coupon extension, so I’m excited to announce that with this change, we now have another Piggybak extension &lt;a href=&#34;https://github.com/piggybak/piggybak_coupons&#34;&gt;piggybak_coupons&lt;/a&gt; available for use with Piggybak. The piggybak_coupon extensions includes support for defining discount type (percent, dollar, or free shipping), discount amoount (for percent and dollar), minimum cart total, expiration date, allowed number of uses. A coupon may be applied on the checkout page via an AJAX lookup. The piggybak_coupons extension is similar to piggybak in that it must be installed as a gem into the application. It includes it&amp;rsquo;s own migration, model, controller, view, and decorator files.&lt;/p&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;Introducing this new architecture gives us the abiliity to incorporate new and custom line item processing functionality. Popular line item types that correspond to popular ecommerce features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;refunds&lt;/li&gt;
&lt;li&gt;gift certificates&lt;/li&gt;
&lt;li&gt;coupons&lt;/li&gt;
&lt;li&gt;bundle discounts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Less common, but still possible with this new architecture might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;custom discounts (e.g. buy one get one free)&lt;/li&gt;
&lt;li&gt;payment via purchase order&lt;/li&gt;
&lt;li&gt;payment via check&lt;/li&gt;
&lt;li&gt;donations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The future for the Piggybak team includes further development of extensions to support some of the common line item type features.&lt;/p&gt;
&lt;p&gt;Naturally, there may be a few follow-up incremental improvements since this was a significant change. All of this work is included in the Piggybak gem release version 0.6.2. Read more about Piggybak and check out the demo &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak: The Roadmap</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/10/piggybak-roadmap/"/>
      <id>https://www.endpointdev.com/blog/2012/10/piggybak-roadmap/</id>
      <published>2012-10-08T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Over the last couple of weeks, a few of us at End Point have had some discussion about the future direction of &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;. Piggybak is an open source mountable ecommerce framework written in Ruby on Rails supported and developed by End Point. It introduces core ecommerce functionality into a Rails application, but is intended to allow the mounted Rails application to maintain control over some architecture elements.&lt;/p&gt;
&lt;h3 id=&#34;pros-of-piggybak&#34;&gt;Pros of Piggybak&lt;/h3&gt;
&lt;p&gt;Until now, the advantage of Piggybak is that it&amp;rsquo;s a fairly lightweight approach. It leverages the power of &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt; rather than creating it&amp;rsquo;s own admin. It allows the mounted Rails application to make decisions on what types of items are sellable and how these items are found (i.e. product finding methods, SSL configuration). Piggybak also has streamlined integration of &lt;a href=&#34;http://activemerchant.org/&#34;&gt;ActiveMerchant&lt;/a&gt;, which immediately provides support of over 40 popular payment gateways. Piggybak has a cookie-based cart and an AJAX-driven one-page checkout.&lt;/p&gt;
&lt;h3 id=&#34;cons-of-piggybak-approach&#34;&gt;Cons of Piggybak Approach&lt;/h3&gt;
&lt;p&gt;Because Piggybak has a lightweight approach, the major disadvantage is that it cannot compete with existing ecommerce frameworks as an out of the box solution with a full ecommerce feature set. When compared with more feature-rich ecommerce platforms like &lt;a href=&#34;https://spreecommerce.org/&#34;&gt;Spree&lt;/a&gt; and Magento these other ecommerce platforms may have more features out of the box. This is a disadvantage because the abstraction, code cleanliness and maintainability provided by Piggybak is not necessarily as strong of a selling point to the feature list to a potential website owner.&lt;/p&gt;
&lt;h3 id=&#34;the-roadmap&#34;&gt;The Roadmap&lt;/h3&gt;
&lt;p&gt;In looking towards the future of Piggybak, we&amp;rsquo;ve decided to build out some features of Piggybak, but will try to maintain a balance between having a good feature set while still maintaining the lightweightedness of Piggybak. Some of our goals in the future include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/piggybak/piggybak_realtime_shipping&#34;&gt;Realtime Shipping with USPS, UPS, and Fedex support&lt;/a&gt; [as extension]&lt;/li&gt;
&lt;li&gt;Improvement of Piggybak installation process [core]&lt;/li&gt;
&lt;li&gt;Advanced Product Optioning Support [as extension]&lt;/li&gt;
&lt;li&gt;Line Item Rearchitecture to support future work on Gift Certificates, Discounts [core]&lt;/li&gt;
&lt;li&gt;Gift Certificate, Discount Support [core]&lt;/li&gt;
&lt;li&gt;Advanced Taxonomy [as extension]&lt;/li&gt;
&lt;li&gt;Reviews &amp;amp; Ratings [as extension]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Follow the Piggybak GitHub user &lt;a href=&#34;https://github.com/piggybak&#34;&gt;here&lt;/a&gt;, check out the website and demo &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;here&lt;/a&gt; and keep an eye out for future blog posts on the progress of Piggybak.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Piggybak: An Update on End Point&#39;s Ruby on Rails Ecommerce Engine</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/09/piggybak-update-on-end-points-ruby-on/"/>
      <id>https://www.endpointdev.com/blog/2012/09/piggybak-update-on-end-points-ruby-on/</id>
      <published>2012-09-24T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;With the recent release of one of our client sites running on &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, Piggybak saw quite a few iterations, both for bug fixes and new feature development. Here are a few updates to Piggybak since its announcement &lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/&#34;&gt;earlier this year&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;admin-continues-to-leverage-railsadmin&#34;&gt;Admin: Continues to Leverage RailsAdmin&lt;/h3&gt;
&lt;p&gt;Piggybak continues to leverage &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt;. RailsAdmin is a customizable admin interface that automagically hooks into your application models. In the case of the recent project completion, the admin was customized to add new features and customize the appearance, which can be done in RailsAdmin with ease.&lt;/p&gt;
&lt;p&gt;As much as I enjoy working with RailsAdmin, I think it would be great in the future to expand the admin support to include other popular Rails admin tools such as &lt;a href=&#34;http://activeadmin.info/&#34;&gt;ActiveAdmin&lt;/a&gt;, which has also gained popularity in the Rails space.&lt;/p&gt;
&lt;h3 id=&#34;refund-adjustments&#34;&gt;Refund Adjustments&lt;/h3&gt;
&lt;p&gt;When Piggybak first came out, there was little in the way to allow orders to be monetarily adjusted in the admin after an order was placed. One requirement that came out of client-driven development was the need for recording refund adjustments. A new model for &amp;ldquo;Adjustments&amp;rdquo; is now included in Piggybak. An arbitrary adjustment can be entered in the admin, which is tied to a specific user, an amount, and a recorded note. This functionality allows for site administrators to record adjustments given against orders.&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;188&#34; src=&#34;/blog/2012/09/piggybak-update-on-end-points-ruby-on/image-0.png&#34; width=&#34;400&#34;/&gt;
&lt;p&gt;At the moment, the creation of an adjustment is &lt;strong&gt;not&lt;/strong&gt; tied to a payment gateway to refund against the original transaction, because ActiveMerchant payment gateways have varied refund support.&lt;/p&gt;
&lt;h3 id=&#34;ajax-queueing-for-shipping-requests-on-one-page-checkout&#34;&gt;AJAX Queueing for Shipping Requests on One-Page Checkout&lt;/h3&gt;
&lt;p&gt;Discussed &lt;a href=&#34;/blog/2012/09/ajax-queuing-in-piggybak/&#34;&gt;in this article&lt;/a&gt;, AJAX queuing was added for shipping method generation on the one-page AJAX-driven checkout.&lt;/p&gt;
&lt;h3 id=&#34;order-notes&#34;&gt;Order Notes&lt;/h3&gt;
&lt;p&gt;Another feature that originated from client needs was the need to record order changes over time. Now included in Piggybak are &amp;ldquo;Order Notes&amp;rdquo;. Order notes are automatically created when attributes or nested attributes are changed on the order:&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;161&#34; src=&#34;/blog/2012/09/piggybak-update-on-end-points-ruby-on/image-1.png&#34; width=&#34;400&#34;/&gt;
&lt;p&gt;And arbitrary order notes can be added to record data not represented in attribute changes:&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;176&#34; src=&#34;/blog/2012/09/piggybak-update-on-end-points-ruby-on/image-2.png&#34; width=&#34;400&#34;/&gt;
&lt;p&gt;All order notes have a created_at attribute and belong to a user, which is tied to the administrator who made the change to the order.&lt;/p&gt;
&lt;h3 id=&#34;masked-cc-number-storage&#34;&gt;Masked CC Number Storage&lt;/h3&gt;
&lt;p&gt;While we don&amp;rsquo;t want to store unencrypted credit card numbers in the database per &lt;a href=&#34;https://www.pcisecuritystandards.org/&#34;&gt;PCI compliance&lt;/a&gt;, Piggybak now includes storage of the masked credit card number to the payments table. This allows the site administrators to examine the credit card type and reference a particular card if needed. This was accomplished using the following method added to the String class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;mask_cc_number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    masked = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.gsub(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\D+/i&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;).match(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^(\d\d)(.+)(\d\d\d\d)$/&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      masked = &lt;span style=&#34;color:#d70&#34;&gt;$1&lt;/span&gt; + &lt;span style=&#34;color:#d70&#34;&gt;$2&lt;/span&gt;.length.times.inject(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;) { |s, i| &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;s&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;*&amp;#34;&lt;/span&gt; } + &lt;span style=&#34;color:#d70&#34;&gt;$3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    masked
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;upgrade-to-rails-328&#34;&gt;Upgrade to Rails 3.2.8&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt; has recently been updated to Rails 3.2.8. No changes were required, as this was considered a minor Rails update.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s exciting to see the progress of Piggybak over the last several months, as well as to see Piggybak launch for a site with complex custom needs. I believe it accomplishes the vision I had in mind for it (&lt;a href=&#34;/blog/2012/09/company-presentation-ecommerce-as-engine/&#34;&gt;summarized in this presentation&lt;/a&gt;), without requiring fighting against assumptions that traditional monolithic ecommerce platforms make. Granted, Piggybak still makes specific ecommerce assumptions, but they are limited to cart, checkout, and order functionality rather than systematic application-level behavior.&lt;/p&gt;
&lt;p&gt;The future likely holds incremental improvements to Piggybak, but there are no plans to change Engine-like structure or principles of Piggybak.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>AJAX Queuing in Piggybak</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/09/ajax-queuing-in-piggybak/"/>
      <id>https://www.endpointdev.com/blog/2012/09/ajax-queuing-in-piggybak/</id>
      <published>2012-09-18T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;AJAX is inherently asynchronous; for the most part, this works fine in web development, but sometimes it can cause problem if you have multiple related AJAX calls that are asynchronous to eachother, such as the use case described in this article.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, a Ruby on Rails open source shopping cart module developed and maintained by End Point, the one page checkout uses AJAX to generate shipping options. Whenever state and zip options change, the shipping address information is sent via AJAX and valid shipping methods are returned and rendered in a select dropdown.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/09/ajax-queuing-in-piggybak/image-0.png&#34; width=&#34;750&#34;/&gt;
&lt;p&gt;Event listeners on the state and zip code inputs trigger to generate shipping options via AJAX.&lt;/p&gt;
&lt;p&gt;While working on development for a client using Piggybak, I came across a scenario where AJAX asynchronous-ity was problematic. Here&amp;rsquo;s how the problematic behavior looked on a timeline, picking up as the user enters their shipping address:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 seconds: User changes state, triggers AJAX shipping lookup with state value, but no zip code entered (Let&amp;rsquo;s refer to this as AJAX REQUEST 1).&lt;/li&gt;
&lt;li&gt;1 second: User changes zip code, triggers AJAX shipping lookup with state and zip value present (Let&amp;rsquo;s refer to this as AJAX REQUEST 2).&lt;/li&gt;
&lt;li&gt;2 seconds: AJAX REQUEST 2 returns valid shipping options.&lt;/li&gt;
&lt;li&gt;3 seconds: AJAX REQUEST 1 returns invalid shipping options, because no zip code provided in this data set, and overwrites shipping options returned by AJAX REQUEST 2.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is that the user has finished entering a valid shipping address, but sees that no valid shipping options can be chosen:&lt;img border=&#34;0&#34; src=&#34;/blog/2012/09/ajax-queuing-in-piggybak/image-1.png&#34; width=&#34;750&#34;/&gt;&lt;/p&gt;
&lt;p&gt;To address this issue, I researched AJAX queuing, with the requirement that AJAX requests should be performed synchronously and existing AJAX requests could be aborted if needed. After experimenting with a few different plugins, I found the most success with the &lt;a href=&#34;http://code.google.com/p/jquery-ajaxq/&#34;&gt;jQuery-ajaxq&lt;/a&gt; plugin. It&amp;rsquo;s simple to use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To append a new AJAX call on to a queue, you call $.ajaxq(queue_name, options), where options includes the standard AJAX arguments.&lt;/li&gt;
&lt;li&gt;To cancel or abort AJAX calls currently running in a queue, you call $.ajaxq(queue_name).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The event listener on state and zip changes now looks like the code shown below, in simplified form. The piggybak.update_shipping_options cancels AJAX requests on the shipping_options queue and then adds a new request to the queue which will execute immediately. This does not affect other asynchronous AJAX requests on the page.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; piggybak = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    update_shipping_options: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $.ajaxq(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipping_options&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $.ajaxq(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;shipping_options&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            url: ...,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            cached: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            data: ...,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dataType: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;JSON&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            beforeSend: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            success: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s how this looks on a timeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 seconds: User changes state, triggers AJAX shipping lookup with state value, but no zip code entered (Let&amp;rsquo;s refer to this as AJAX REQUEST 1).&lt;/li&gt;
&lt;li&gt;1 second: User changes zip code, triggers AJAX shipping lookup with state and zip value present (Let&amp;rsquo;s refer to this as AJAX REQUEST 2). This signals to abort all current AJAX requeusts on the shipping_options queue.&lt;/li&gt;
&lt;li&gt;2 seconds: AJAX REQUEST 2 returns valid shipping options.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was an interesting technical problem to solve, but the needs were not surprising. The &lt;a href=&#34;http://code.google.com/p/jquery-ajaxq/&#34;&gt;jQuery-ajaxq plugin&lt;/a&gt; offers a simple, elegant solution for handling AJAX queing in jQuery.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Company Presentation: Ecommerce as an Engine</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/09/company-presentation-ecommerce-as-engine/"/>
      <id>https://www.endpointdev.com/blog/2012/09/company-presentation-ecommerce-as-engine/</id>
      <published>2012-09-14T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;iframe frameborder=&#34;0&#34; height=&#34;500&#34; marginheight=&#34;0&#34; marginwidth=&#34;0&#34; scrolling=&#34;no&#34; src=&#34;http://www.slideshare.net/slideshow/embed_code/14290723?hostedIn=slideshare&amp;page=upload&#34; width=&#34;750&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;Today, I gave the presentation to my coworkers entitled &amp;ldquo;Puppies &amp;amp; Ecommerce as an Engine&amp;rdquo;. The presentation is strongly coupled with my work on &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, and includes a discussion of traditional ecommerce platforms versus a lightweight ecommerce approach through modularity (Rails Engine). It also provides some code examples as how this does work in Piggybak.&lt;/p&gt;
&lt;p&gt;Below are a few more related articles to my work on Piggybak. Check them out!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/09/musica-russica-launches-piggybak/&#34;&gt;Recent Launch of Musica Russica on Piggybak&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/&#34;&gt;Introduction of Piggybak&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/01/activerecord-callbacks-ecommerce-order/&#34;&gt;Leveraging ActiveRecord Callbacks in Ecommerce&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2011/03/ecommerce-sinatra-shopping-cart/&#34;&gt;Shopping Cart Options (applied to a Sinatra application)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Cannot parse Cookie header in Ruby on Rails</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/09/cannot-parse-cookie-header-in-ruby-on/"/>
      <id>https://www.endpointdev.com/blog/2012/09/cannot-parse-cookie-header-in-ruby-on/</id>
      <published>2012-09-07T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Yesterday I resolved a client emergency for a Ruby on Rails site that continues to leave me scratching my head, even with follow-up investigation. In short, the emergency came up after an email marketing campaign was sent out in the morning, and resulted in server (HTTP 500 Status Code) errors for every customer that clicked on the email links. Despite the fact that Rails exception emails are sent to the client and me, the errors were never reaching the exception email code, so I was unaware of the emergency until the client contacted me.&lt;/p&gt;
&lt;p&gt;Upon jumping on the server, I saw this in the production log repeatedly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ArgumentError (cannot parse Cookie header: invalid %-encoding (...)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ArgumentError (cannot parse Cookie header: invalid %-encoding (...)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ArgumentError (cannot parse Cookie header: invalid %-encoding (...)):&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The URLs that the production log was complaining about had a bunch of Google Analytics tracking variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;utmcmd=Email&lt;/li&gt;
&lt;li&gt;utmcct=customeremail&lt;/li&gt;
&lt;li&gt;utmccn=New Site Sale 70% off&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After a user visits the site, these variables are typically stored as cookies for Google Analytics tracking. Upon initial investigation, the issue appeared to be triggered from any Google campaign variable that contained a &amp;lsquo;%&amp;rsquo; character.&lt;/p&gt;
&lt;p&gt;After follow-up investigation today, the more complete story looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Email blast sent&lt;/li&gt;
&lt;li&gt;User clicks on link in email. That link goes to the email marketing company first for tracking, then is redirected to the website.&lt;/li&gt;
&lt;li&gt;According to the email marketing campaign (after chatting with them today), Google Analytics tacks on their own tracking here, which is the source of the non-parseable URLs.&lt;/li&gt;
&lt;li&gt;Rack receives the request and tries to parse the query, utilizing the Ruby URI module:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;decode_www_form_component&lt;/span&gt;(str, enc=&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Encoding&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;UTF_8&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;TBLDECWWWCOMP_&lt;/span&gt;.empty?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      tbl = {} 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;256&lt;/span&gt;.times &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |i|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        h, l = i&amp;gt;&amp;gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, i&amp;amp;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;15&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tbl[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%%%X%X&amp;#39;&lt;/span&gt; % [h, l]] = i.chr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tbl[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%%%x%X&amp;#39;&lt;/span&gt; % [h, l]] = i.chr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tbl[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%%%X%x&amp;#39;&lt;/span&gt; % [h, l]] = i.chr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tbl[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%%%x%x&amp;#39;&lt;/span&gt; % [h, l]] = i.chr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      tbl[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;TBLDECWWWCOMP_&lt;/span&gt;.replace(tbl)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;TBLDECWWWCOMP_&lt;/span&gt;.freeze
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rescue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ArgumentError&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;invalid %-encoding (&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;str&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;)&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\A[^%]*(?:%\h\h[^%]*)*\z/&lt;/span&gt; =~ str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    str.gsub(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\+|%\h\h/&lt;/span&gt;, &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;TBLDECWWWCOMP_&lt;/span&gt;).force_encoding(enc)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;The argument error on line 18 trickles up the pipeline in &lt;a href=&#34;http://rack.github.com/&#34;&gt;rack&lt;/a&gt;, and is not handled elegantly, so a rack-originated server (HTTP 500 Status Code) error is triggered. Again, the &amp;lsquo;%&amp;rsquo; character in the URL appears to be the problem here likely based on the regexp match on line 18 — the error is not triggered when the Google variable does not contain a &amp;lsquo;%&amp;rsquo; character.&lt;/li&gt;
&lt;li&gt;Customer sees server error page and is unhappy :(&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At the time of the emergency we tried solving the problem on multiple avenues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Investigated removal of Google Analytics tracking URLs from email blast links, but this wouldn&amp;rsquo;t help all the customers who already received the emails.&lt;/li&gt;
&lt;li&gt;Remove CGI parameters or sanitize them via nginx.&lt;/li&gt;
&lt;li&gt;Make web application changes to ignore or handle the ArgumentError.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ultimately, I ended up added a begin/rescue statement to the rack code to skip escaping URLs where decode_www_form_component was raising an error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;unescape&lt;/span&gt;(s, encoding = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Encoding&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;UTF_8&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;URI&lt;/span&gt;.decode_www_form_component(s, encoding)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rescue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;.logger.warn &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DECODING on &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;s.inspect&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; with &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;encoding.inspect&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; FAILING.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While this is a reasonable fix, I&amp;rsquo;m still puzzled for a number of reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The email marketing company (via chatting) claims that Google Analytics is tacking on the tracking variables, however, there is only one redirect from the email marketing company to the website. I don&amp;rsquo;t understand the mechanism for which Google Analytics tracking variables are added to the URL, and if this process can be cleaned up to ensure proper URL encoding.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m not sure if the issue happens immediately upon a customer landing on the site, or after a cookie is stored.&lt;/li&gt;
&lt;li&gt;At the moment, I&amp;rsquo;m not able to reproduce this issue in development mode, which makes it difficult to troubleshoot on my development instance.&lt;/li&gt;
&lt;li&gt;When I use the URI module directly in a console, no ArgumentError is raised:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;URI&lt;/span&gt;.decode_www_form_component(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url_with_google_campaign_variables&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt; &lt;span style=&#34;color:#888&#34;&gt;#happy dance&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My best advice at this point is to tell the client not to use &amp;lsquo;%&amp;rsquo; character in the Google Campaign ID, but I&amp;rsquo;m still putting all the pieces together in the virtual code map in my head. I think a fix is more likely needed on the Ruby and rack side to handle URL parameters with the &amp;lsquo;%&amp;rsquo; character, and to elegantly handle situations where the URI.decode_www_form_components method dies.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Musica Russica Launches with Piggybak</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/09/musica-russica-launches-piggybak/"/>
      <id>https://www.endpointdev.com/blog/2012/09/musica-russica-launches-piggybak/</id>
      <published>2012-09-03T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;http://www.musicarussica.com/&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/09/musica-russica-launches-piggybak/image-0.png&#34; width=&#34;745&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The new home page for Musica Russica.&lt;/p&gt;
&lt;p&gt;Last week, we launched a new site for &lt;a href=&#34;http://www.musicarussica.com/&#34;&gt;Musica Russica&lt;/a&gt;. The old site was running on an outdated version of Lasso and Filemaker and was approximately 15 years old. Although it was still chugging along, finding hosting support and developers for an outdated platform becomes increasingly challenging as time goes on. The new site runs on &lt;a href=&#34;http://rubyonrails.org/&#34;&gt;Ruby on Rails 3&lt;/a&gt; with &lt;a href=&#34;http://nginx.org/&#34;&gt;Nginx&lt;/a&gt; and &lt;a href=&#34;http://unicorn.bogomips.org/&#34;&gt;Unicorn&lt;/a&gt; and uses open source Rails gems &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt;, &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, &lt;a href=&#34;https://github.com/ryanb/cancan&#34;&gt;CanCan&lt;/a&gt; and &lt;a href=&#34;https://github.com/plataformatec/devise&#34;&gt;Devise&lt;/a&gt;. RailsAdmin is a great open source Rails Admin tool that I&amp;rsquo;ve blogged about before (&lt;a href=&#34;/blog/2011/08/railsadmin-gem-ecommerce/&#34;&gt;here&lt;/a&gt;, &lt;a href=&#34;/blog/2012/03/cancan-railsadmin/&#34;&gt;here&lt;/a&gt;, and &lt;a href=&#34;/blog/2012/02/railsadmin-import-part-2/&#34;&gt;here&lt;/a&gt;). Piggybak is End Point&amp;rsquo;s home grown light-weight ecommerce platform, also blogged about several times (&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/&#34;&gt;here&lt;/a&gt;, &lt;a href=&#34;/blog/2012/01/activerecord-callbacks-ecommerce-order/&#34;&gt;here&lt;/a&gt;, and &lt;a href=&#34;/blog/2012/06/rbenv-passenger-upgrade/&#34;&gt;here&lt;/a&gt;). Below are a few more details on the site:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The site includes Rails 3 goodness such as an elegant and thorough MVC architecture, advanced routing to encourage clean, user-friendly URLs, the ability to integrate modular elements (Piggybak, RailsAdmin) with ease, and several built-in performance options. The site also features a few other popular Rails gems such as &lt;a href=&#34;http://prawn.majesticseacreature.com/&#34;&gt;Prawn&lt;/a&gt; (for printing order and packing slip PDFs), &lt;a href=&#34;https://github.com/tobmatth/rack-ssl-enforcer&#34;&gt;Rack-SSL-Enforcer&lt;/a&gt; (a nice tool for enforcing SSL pages), &lt;a href=&#34;https://github.com/rails/exception_notification&#34;&gt;exception_notification&lt;/a&gt; (a small tool to configure sending emails on Rails exceptions), and &lt;a href=&#34;https://github.com/airblade/paper_trail&#34;&gt;Paper Trail&lt;/a&gt; (a gem to track changes on your models).&lt;/li&gt;
&lt;li&gt;The site features complex, feature-rich search run by &lt;a href=&#34;http://sphinxsearch.com/&#34;&gt;Sphinx&lt;/a&gt; with the popular Rails gem &lt;a href=&#34;http://pat.github.com/ts/en/&#34;&gt;ThinkingSphinx&lt;/a&gt;. The search includes standard features such as selecting items listed per page, sort by various product attributes and wildcard text search. Although we&amp;rsquo;ve used Solr on several recent Rails projects, we wanted to go with Sphinx here to avoid the Tomcat and Java requirements. After working with both in depth, I&amp;rsquo;d conclude that both Sphinx and Solr offer very similar features.&lt;/li&gt;
&lt;li&gt;The site has a very custom data model. With the use of &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, any Rails model can become a sellable item, which gives us the ability to customize each data model without affecting all sellable products. This opportunity suits Musica Russica well because they offer very different product types such as books, sheet music, CDs, and sheet music collections. Additional features (taxonomy, cross-sell, upsell) in the application are not constrained to assumptions that traditional monolithic ecommerce applications make.&lt;/li&gt;
&lt;li&gt;The site introduces downloadable products, described more &lt;a href=&#34;/blog/2012/02/download-functionality-rails-ecommerce/&#34;&gt;in this article&lt;/a&gt;. A user purchasing downloadable products is required to register, and then has access to their purchased downloadables after purchase. Downloadable orders also include free shipping. Piggybak needed minor customization to support downloadable products.&lt;/li&gt;
&lt;li&gt;The site also features integration with the &lt;a href=&#34;http://gateway.elavon.com/&#34;&gt;Elavon&lt;/a&gt; payment gateway. This is the first time that End Point has worked with Elavon. Elavon is supported by ActiveMerchant, so adding support for Elavon simply required hooking into ActiveMerchant&amp;rsquo;s Elavon module.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was exciting to build a custom Rails ecommerce site from the ground-up, and the site certainly showcases End Point&amp;rsquo;s wide range of ecommerce expertise ranging from hosting with modern cutting-edge servers to development of popular front-end ecommerce features with advanced open source tools.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>.rbenv and Passenger: Working through an Upgrade</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/rbenv-passenger-upgrade/"/>
      <id>https://www.endpointdev.com/blog/2012/06/rbenv-passenger-upgrade/</id>
      <published>2012-06-22T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Yesterday, I worked on upgrading the &lt;a href=&#34;https://github.com/piggybak/demo&#34;&gt;Piggybak demo&lt;/a&gt; application, which runs on &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, an open source Ruby on Rails ecommerce plugin developed and maintained by End Point. The demo was running on Ruby 1.8.7 and Rails 3.1.3, but I wanted to update it to Ruby 1.9.* and Rails 3.2.6 to take advantage of improved performance in Ruby and the recent Rails security updates. I also wanted to update the Piggybak version, since there have been &lt;a href=&#34;https://github.com/piggybak/piggybak/commits/master&#34;&gt;several recent bug fixes and commits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the constraints with the upgrade was that I wanted to upgrade via &lt;a href=&#34;https://github.com/rbenv/rbenv&#34;&gt;.rbenv&lt;/a&gt;, because End Point has been happily using .rbenv recently. Below are the steps &lt;a href=&#34;/team/richard-templet/&#34;&gt;Richard&lt;/a&gt; and I went through for the upgrade, as well as a minor Passenger issue.&lt;/p&gt;
&lt;h3 id=&#34;step-1-rbenv-installation&#34;&gt;Step 1: .rbenv Installation&lt;/h3&gt;
&lt;p&gt;First, I followed the instructions &lt;a href=&#34;https://github.com/rbenv/rbenv&#34;&gt;here&lt;/a&gt; to install rbenv and Ruby 1.9.3 locally under the user that Piggybak runs under (let’s call it the &lt;strong&gt;steph&lt;/strong&gt; user). I set the local Ruby version to my local install. I also installed bundler using the local Ruby version.&lt;/p&gt;
&lt;h3 id=&#34;step-2-bundle-update&#34;&gt;Step 2: bundle update&lt;/h3&gt;
&lt;p&gt;Next, I blew away the existing bundle config for my application, as well as the installed bundler gem files for the application. I followed the standard steps to install and update the new gems with the local updated Ruby and updated Rails. Then I restarted the app.&lt;/p&gt;
&lt;h3 id=&#34;step-3-fail&#34;&gt;Step 3: Fail&lt;/h3&gt;
&lt;p&gt;At this point, my application would not restart, and the backtrace complained of a Passenger issue, &lt;em&gt;and&lt;/em&gt; it referenced Ruby 1.8. Richard and I investigated the errors and concluced that the application’s Passenger configuration was still referencing the system Ruby install and the outdated Passenger installation.&lt;/p&gt;
&lt;p&gt;Here’s where I hit the catch 22: I needed root access to update the passenger.conf as well as to install Passenger against Ruby 1.9.3. This defeated the purpose of using .rbenv and working with a local Ruby install only.&lt;/p&gt;
&lt;h3 id=&#34;step-4-local-passenger-installation&#34;&gt;Step 4: Local Passenger Installation&lt;/h3&gt;
&lt;p&gt;To install Passenger against the local Ruby version, I decided to install it as the &lt;strong&gt;steph&lt;/strong&gt; user. First, I installed the gem:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gem install passenger&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, I went to the local installed version of Passenger to run the installation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; /home/steph/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/passenger-3.0.13/bin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./passenger-install-apache2-module&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, I copied the passenger installation output to the passenger.conf file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   LoadModule passenger_module /home/steph/.rbenv/versions/1.9.3-p194/lib/ruby/gems/\
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     1.9.1/gems/passenger-3.0.13/ext/apache2/mod_passenger.so
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   PassengerRoot /home/steph/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/passenger-3.0.13
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   PassengerRuby /home/steph/.rbenv/versions/1.9.3-p194/bin/ruby&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With a server restart, the Piggybak demo was up and running on updated Ruby and Rails!&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/06/rbenv-passenger-upgrade/image-0.png&#34; width=&#34;600&#34;/&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Retrospectively, I could have avoided the Passenger issue by installing Ruby 1.9.3 on the server as root, because there isn’t much else on the server. But I like using .rbenv and it’s possible that a Passenger upgrade won’t be required with every Ruby update, so the new Passenger configuration is acceptable [to me, for now].&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Why Piggybak exists</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/why-piggybak-exists/"/>
      <id>https://www.endpointdev.com/blog/2012/06/why-piggybak-exists/</id>
      <published>2012-06-13T00:00:00+00:00</published>
      <author>
        <name>Brian Dillon</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;/blog/2012/06/why-piggybak-exists/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;clear:right; float:right; margin-left:1em; margin-bottom:1em&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;320&#34; src=&#34;/blog/2012/06/why-piggybak-exists/image-0.png&#34; width=&#34;205&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are some clients debating between using Spree, an e-commerce platform, and a homegrown Rails solution for an e-commerce application.&lt;/p&gt;
&lt;p&gt;E-commerce platforms are monolithic—​they try to solve a lot of different problems at once. Also, many of these e-commerce platforms frequently make premature decisions before getting active users on it. One way of making the features of a platform match up better to a user’s requirements is to get a minimal viable product out quick and grow features incrementally.&lt;/p&gt;
&lt;p&gt;Piggybak was created by first trying to identify the most stable and consistent features of a shopping cart. Here are the various pieces of a cart to consider.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shipping&lt;/li&gt;
&lt;li&gt;Tax&lt;/li&gt;
&lt;li&gt;CMS Features&lt;/li&gt;
&lt;li&gt;Product Search&lt;/li&gt;
&lt;li&gt;Cart / Checkout&lt;/li&gt;
&lt;li&gt;Product Features&lt;/li&gt;
&lt;li&gt;Product Taxonomy&lt;/li&gt;
&lt;li&gt;Discount Sales&lt;/li&gt;
&lt;li&gt;Rights and Roles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What doesn’t vary? &lt;strong&gt;Cart &amp;amp; Checkout.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Shipping, tax, product catalog design, sales promotions, and rights and roles all vary across different e-commerce sites. The only strict commonality is the cart and the checkout.&lt;/p&gt;
&lt;p&gt;Piggybak is just the cart and checkout.&lt;/p&gt;
&lt;p&gt;You mount Piggybak as a gem into any Rails app, and can assign any object as a purchasable product using a the tag “acts_as_variant” and you’re good to go. To learn more, and to see it in action ‘checkout’ &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak on GitHub&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Download Functionality for Rails Ecommerce</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/02/download-functionality-rails-ecommerce/"/>
      <id>https://www.endpointdev.com/blog/2012/02/download-functionality-rails-ecommerce/</id>
      <published>2012-02-08T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently had to build out downloadable product support for a client project running on &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak (a Ruby on Rails Ecommerce engine)&lt;/a&gt; with extensive use of &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt;. Piggybak’s core functionality does not support downloadable products, but it was not difficult to extend. Here are some steps I went through to add this functionality. While the code examples apply specifically to a Ruby on Rails application using &lt;a href=&#34;https://github.com/thoughtbot/paperclip&#34;&gt;paperclip&lt;/a&gt; for managing attachments, the general steps here would apply across languages and frameworks.&lt;/p&gt;
&lt;h3 id=&#34;data-migration&#34;&gt;Data Migration&lt;/h3&gt;
&lt;p&gt;Piggybak is a pluggable ecommerce engine. To make any models inside your application “sellable”, the class method acts_as_variant must be called for any class. This provides a nice flexibility in defining various sellable models throughout the application. Given that I will sell tracks in this example, my first step to supporting downloadable content is adding an is_downloadable boolean and attached file fields to the migration for a sellable item. The migration looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CreateTracks&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveRecord&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Migration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;change&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    create_table &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:tracks&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |t|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;# a bunch of fields specific to tracks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      t.boolean &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:is_downloadable&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:nil&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:default&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      t.string &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:downloadable_file_name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      t.string &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:downloadable_content_type&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      t.string &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:downloadable_file_size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      t.string &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:downloadable_updated_at&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;class-definitions&#34;&gt;Class Definitions&lt;/h3&gt;
&lt;p&gt;Next, I update my class definition to make tracks sellable and hook in paperclip functionality:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Track&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveRecord&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  acts_as_variant
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_attached_file &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:downloadable&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:path&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;:rails_root/downloads/:id/:basename.:extension&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:url&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;downloads/:id/:basename.:extension&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The important thing to note here is that the attached downloadable files &lt;strong&gt;must not&lt;/strong&gt; be stored in the public root. Why? Because we don’t want users to access the files via a URL through the public root. Downloadable files will be served via the send_file call, discussed below.&lt;/p&gt;
&lt;h3 id=&#34;shipping&#34;&gt;Shipping&lt;/h3&gt;
&lt;p&gt;Piggybak’s order model has_many shipments. In the case of an order that contains only downloadables, shipments can be empty. To accomplish this, I extend the Piggybak::Cart model using &lt;a href=&#34;http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/&#34;&gt;ActiveSupport::Concern&lt;/a&gt; to check whether or not an order is downloadable, with the following instance method:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CartDecorator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;extend&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveSupport&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Concern&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;InstanceMethods&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;is_downloadable?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      items = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.items.collect { |li| li[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt;].item }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      items.all? { |i| i.is_downloadable }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Cart&lt;/span&gt;.send(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:include&lt;/span&gt;, &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CartDecorator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all of the cart items are downloadable, the order is considered downloadable and no shipment is generated for this order. With this cart method, I show the FREE! value on the checkout page under shipping methods.&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;213&#34; src=&#34;/blog/2012/02/download-functionality-rails-ecommerce/image-0.png&#34; width=&#34;400&#34;/&gt;
&lt;h3 id=&#34;forcing-log-in&#34;&gt;Forcing Log In&lt;/h3&gt;
&lt;p&gt;The next step for adding downloadable support is to add code to enforce user log in. In this particular project, I assume that downloads are not included as attachments in files since the files may be extremely large. I add a has_downloadable method used to enforce log in:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CartDecorator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;extend&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveSupport&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Concern&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;InstanceMethods&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;has_downloadable?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      items = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.items.collect { |li| li[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt;].item }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      items.any? { |i| i.is_downloadable }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Piggybak&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Cart&lt;/span&gt;.send(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:include&lt;/span&gt;, &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CartDecorator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On the checkout page, a user is forced to log in if cart.has_downloadable?. After log in, the user bounces back to the checkout page.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;
&lt;img border=&#34;0&#34; height=&#34;177&#34; src=&#34;/blog/2012/02/download-functionality-rails-ecommerce/image-1.png&#34; width=&#34;400&#34;/&gt;&lt;/div&gt;
&lt;h3 id=&#34;download-list-page&#34;&gt;Download List Page&lt;/h3&gt;
&lt;p&gt;After a user has purchased downloadable products, they’ll need a way to access these files. Next, I create a downloads page which lists orders and their downloads:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;
&lt;a href=&#34;/blog/2012/02/download-functionality-rails-ecommerce/image-2-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;380&#34; src=&#34;/blog/2012/02/download-functionality-rails-ecommerce/image-2.png&#34; width=&#34;400&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;With a user instance method (current_user.downloads_by_order), the download index page iterates through orders with downloads to display orders and their downloads. The user method for generating orders and downloads shown here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;User&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveRecord&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;downloads_by_order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.piggybak_orders.inject([]) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |arr, order|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      downloads = []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      order.line_items.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |line_item|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        downloads &amp;lt;&amp;lt; line_item.variant.item &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; line_item.variant.item.is_downloadable?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      arr &amp;lt;&amp;lt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order&lt;/span&gt; =&amp;gt; order,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:downloads&lt;/span&gt; =&amp;gt; downloads
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; downloads.any?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      arr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above method would be a good candidate for Rails low-level caching or alternative caching which should be cleared after user purchases to minimize download lookup.&lt;/p&gt;
&lt;h3 id=&#34;sending-files&#34;&gt;Sending Files&lt;/h3&gt;
&lt;p&gt;As I mentioned above, download files should not be stored in the public directory for public access. From the download list page, the “Download Now” link maps to the following method in the downloads controller:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DownloadsController&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ApplicationController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;show&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    item = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ProductType&lt;/span&gt;.find(params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; current_user.downloads.include?(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      send_file &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;.root&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;item.downloadable.url(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:default&lt;/span&gt;, &lt;span style=&#34;color:#080&#34;&gt;false&lt;/span&gt;)&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      redirect_to(root_url, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:notice&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;You do not have access to this content.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that there is additional verification here to check if the current user’s downloads includes the download requested. The .url(:default, false) bit hides paperclip’s cache buster (e.g. “?123456789”) from the url in order to send the file.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This straightforward code accomplished the  major updates required for download support: storing and sending the file, enforcing login, and handling shipping. In some cases, download support functionality may be more advanced, but the elements described here make up the most basic building blocks.&lt;/p&gt;
&lt;p&gt;If you are interested in this project, check out these related articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/&#34;&gt;Introducing Piggybak: A Mountable Ruby on Rails Ecommerce Engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/01/activerecord-callbacks-ecommerce-order/&#34;&gt;ActiveRecord Callbacks for Order Processing in Ecommerce Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/01/import-railsadmin/&#34;&gt;Importing into RailsAdmin: Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2012/02/railsadmin-import-part-2/&#34;&gt;Importing into RailsAdmin: Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>RailsAdmin Import: Part 2</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/02/railsadmin-import-part-2/"/>
      <id>https://www.endpointdev.com/blog/2012/02/railsadmin-import-part-2/</id>
      <published>2012-02-01T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently wrote about &lt;a href=&#34;/blog/2012/01/import-railsadmin/&#34;&gt;importing data in RailsAdmin&lt;/a&gt;. &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt; is a Rails engine that provides a nice admin interface for managing your data, which comes packed with configuration options.&lt;/p&gt;
&lt;p&gt;In a recent Ruby on Rails ecommerce project, I’ve been using RailsAdmin, &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak (a Rails ecommerce gem supported by End Point)&lt;/a&gt;, and have been building out custom front-end features such as advanced search and downloadable product support. When this client came to End Point with the project, we offered several options for handling data migration from a legacy system to the new Rails application:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a standard migration file, which migrates data from the existing legacy database to the new data architecture. The advantage with this method is that it requires virtually no manual interaction for the migration process. The disadvantage with this is that it’s basically a one-off solution and would never be useful again.&lt;/li&gt;
&lt;li&gt;Have the client manually enter data. This was a reasonable solution for several of the models that required 10 or less entries, but not feasible for the tables containing thousands of entries.&lt;/li&gt;
&lt;li&gt;Develop import functionality to plug into RailsAdmin which imports from CSV files. The advantage to this method is that it could be reused in the future. The disadvantage with ths method is that data exported from the legacy system would have to be cleaned up and formatted for import.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The client preferred option #3. Using &lt;a href=&#34;https://github.com/sferik/rails_admin/wiki/Custom-action&#34;&gt;a quick script for generating custom actions for RailsAdmin&lt;/a&gt;, I developed a new gem called rails_admin_import to handle import that could be plugged into RailsAdmin. Below are some technical details on the generic import solution.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2012/02/railsadmin-import-part-2/image-0.png&#34; width=&#34;750&#34;/&gt;
&lt;h3 id=&#34;activesupportconcern&#34;&gt;ActiveSupport::Concern&lt;/h3&gt;
&lt;p&gt;Using &lt;a href=&#34;http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/&#34;&gt;ActiveSupport::Concern&lt;/a&gt;, the rails_admin_import gem extends ActiveRecord::Base to add the following class methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;import_fields: Returns an array of fields that will be included in the import, excluding :id, :created_at, and :updated_at, belongs_to fields, and file fields.&lt;/li&gt;
&lt;li&gt;belongs_to_fields: Returns an array of fields with belongs_to relationships to other models.&lt;/li&gt;
&lt;li&gt;many_to_many_fields: Returns an array of fields with has_and_belongs_to_many relationships to other models.&lt;/li&gt;
&lt;li&gt;file_fields: Returns an array of fields that represent data for &lt;a href=&#34;https://github.com/thoughtbot/paperclip&#34;&gt;Paperclip&lt;/a&gt; attached files.&lt;/li&gt;
&lt;li&gt;run_import: Method for running the actual import, receives request params.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the following instance methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;import_files: sets attached files for object&lt;/li&gt;
&lt;li&gt;import_belongs_to_data: sets belongs_to associated data for object&lt;/li&gt;
&lt;li&gt;import_many_to_many_data: sets many_to_many associated data for object&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The general approach here is that the import of files, belongs_to, many_to_many relationships, and standard fields makes up the import process for a single object. The run_import method collects success and failure messages for each object import attempt and those results are presented to the user. A regular &lt;a href=&#34;http://api.rubyonrails.org/classes/ActiveRecord/Base.html&#34;&gt;ActiveRecord&lt;/a&gt; save method is called on the object, so the existing validation of objects during each save applies.&lt;/p&gt;
&lt;h3 id=&#34;working-with-associated-data&#34;&gt;Working with Associated Data&lt;/h3&gt;
&lt;p&gt;One of the tricky parts here is how to handle import of fields representing associations. Given a user model that belongs to a state, country, and has many roles, how would one decide what state, country, or role value to include in the import?&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;167&#34; src=&#34;/blog/2012/02/railsadmin-import-part-2/image-1.png&#34; width=&#34;400&#34;/&gt;
&lt;p&gt;I’ve solved this by including a dropdown to select the attribute used for mapping in the form. Each of the dropdowns contains a list of model attributes that are used for association mapping. A user can then select the associated mappings when they upload a file. In a real-life situation, I may import the state data via abbreviation, country via display name (e.g. “United States”, “Canada”) and role via the role name (e.g. “admin”). My data import file might look like this:&lt;/p&gt;
&lt;table cellpadding=&#34;0&#34; cellspacing=&#34;0&#34; style=&#34;border:1px solid #999;&#34; width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;email&lt;/td&gt;
&lt;td&gt;favorite_color&lt;/td&gt;
&lt;td&gt;state&lt;/td&gt;
&lt;td&gt;country&lt;/td&gt;
&lt;td&gt;role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Steph Skardal&lt;/td&gt;
&lt;td&gt;steph@endpoint.com&lt;/td&gt;
&lt;td&gt;blue&lt;/td&gt;
&lt;td&gt;CO&lt;/td&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aleks Skardal&lt;/td&gt;
&lt;td&gt;aleksskardal@gmail.com&lt;/td&gt;
&lt;td&gt;green&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Norway&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roger Skardal&lt;/td&gt;
&lt;td&gt;roger@gmail.com&lt;/td&gt;
&lt;td&gt;tennis ball yellow&lt;/td&gt;
&lt;td&gt;UT&lt;/td&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;dog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Milton Skardal&lt;/td&gt;
&lt;td&gt;milton@gmail.com&lt;/td&gt;
&lt;td&gt;kibble brown&lt;/td&gt;
&lt;td&gt;UT&lt;/td&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;dog&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;many-to-many-relationships&#34;&gt;Many to Many Relationships&lt;/h3&gt;
&lt;p&gt;Many to many relationships are handled by allowing multiple columns in the CSV to correspond to the imported data. For example, there may be two columns for role on the user import, where users may be assigned to multiple roles. This may not be suitable for data with a large number of many to many assignments.&lt;/p&gt;
&lt;h3 id=&#34;import-of-file-fields&#34;&gt;Import of File Fields&lt;/h3&gt;
&lt;p&gt;In this scenario, I’ve chosen to use open-uri to request existing files from a URL. The CSV must contain the URL for that file to be imported. The import process downloads the file and attaches it to the imported object.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.class.file_fields.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; map[key] &amp;amp;&amp;amp; !row[map[key]].nil?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      row[map[key]] = row[map[key]].gsub(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\s+/&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#038&#34;&gt;format&lt;/span&gt; = row[map[key]].match(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/[a-z0-9]+$/&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;.root&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/tmp/uploads/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.permalink&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;format&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;) { |file| file &amp;lt;&amp;lt; &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt;(row[map[key]]).read }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.send(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;key&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;=&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;File&lt;/span&gt;.open(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;.root&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/tmp/uploads/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.permalink&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;format&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rescue&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Exception&lt;/span&gt; =&amp;gt; e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.errors.add(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:base&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Import error: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;e.inspect&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the file request fails, an error is added to the object and presented to the user. This method may not be suitable for handling files that do not currently exist on a web server, but it was suitable for migrating a legacy application.&lt;/p&gt;
&lt;h3 id=&#34;configuration-display&#34;&gt;Configuration: Display&lt;/h3&gt;
&lt;p&gt;Following RailsAdmin’s example for setting configurations, I’ve added the ability to allow the import display to be set for each model.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.model &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;User&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  label &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above configuration will yield success and error messages with the user.name, e.g.:&lt;/p&gt;
&lt;img border=&#34;0&#34; height=&#34;244&#34; src=&#34;/blog/2012/02/railsadmin-import-part-2/image-2.png&#34; width=&#34;400&#34;/&gt;
&lt;h3 id=&#34;configuration-excluded-fields&#34;&gt;Configuration: Excluded Fields&lt;/h3&gt;
&lt;p&gt;In addition to allowing a configurable display option, I’ve added the configuration for excluding fields.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.model &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;User&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  excluded_fields &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:reset_password_token&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:reset_password_sent_at&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:remember_created_at&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sign_in_count&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:current_sign_in_at&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:last_sign_in_at&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:current_sign_in_ip&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:last_sign_in_ip&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above configuration will exclude the specified fields during the import, and they will not display on the import page.&lt;/p&gt;
&lt;h3 id=&#34;configuration-additional-fields-and-additional-processing&#34;&gt;Configuration: Additional Fields and Additional Processing&lt;/h3&gt;
&lt;p&gt;Another piece of functionality that I found necessary for various imports was to hook in additional import functionality. Any model can have an instance method &lt;strong&gt;before_import_save&lt;/strong&gt; that accepts the row of CSV data and map of CSV keys to perform additional tasks. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;before_import_save&lt;/span&gt;(row, map)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.created_nested_items(row, map)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above method will create nested items during the import process. This simple extensibility allows for additional data to be handled upon import outside the realm of has_and_belongs_to and belongs_to relationships.&lt;/p&gt;
&lt;p&gt;Fields for additional nested data can be defined with the extra_fields configuration, and are shown on the import page.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.model &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;User&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  extra_fields &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:field1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:field2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:field3&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:field4&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;hooking-into-railsadmin&#34;&gt;Hooking into RailsAdmin&lt;/h3&gt;
&lt;p&gt;As I mentioned above, I used a script to generate this Engine. Using RailsAdmin configurable actions, import must be added as an action:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.actions &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  dashboard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  index
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  import
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And &lt;a href=&#34;https://github.com/ryanb/cancan&#34;&gt;CanCan&lt;/a&gt; settings must be updated to allow for import if applicable, e.g.:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cannot &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:import&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;can &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:import&lt;/span&gt;, &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;My goal in developing this tool was to produce reusable functionality that could easily be applied to multiple models with different import needs, and to use this tool across Rails applications. I’ve already used this gem in another Rails 3.1 project to quickly import data that would otherwise be difficult to deal with manually. The combination of association mapping and configurability produces a flexibility that encourages reusability.&lt;/p&gt;
&lt;p&gt;Feel free to review or check out the code &lt;a href=&#34;https://github.com/stephskardal/rails_admin_import&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>ActiveRecord Callbacks for Order Processing in Ecommerce Applications</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/01/activerecord-callbacks-ecommerce-order/"/>
      <id>https://www.endpointdev.com/blog/2012/01/activerecord-callbacks-ecommerce-order/</id>
      <published>2012-01-13T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;As I recently blogged about, I introduced a new &lt;a href=&#34;http://piggybak.org&#34;&gt;Ruby on Rails Ecommerce Engine&lt;/a&gt;. The gem relies on &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt;, a Ruby on Rails engine that provides a nice interface for managing data. Because the RailsAdmin gem drives order creation on the backend in the context of a standard but configurable CRUD interface, and because I didn’t want to hack at the RailsAdmin controllers, much of the order processing logic leverages ActiveRecord callbacks for processing. In this blog article, I’ll cover the process that happens when an order is saved.&lt;/p&gt;
&lt;h3 id=&#34;order-data-model&#34;&gt;Order Data Model&lt;/h3&gt;
&lt;p&gt;The first thing to note is the data model and the use of nested attributes. Here’s how the order model relates to its associated models:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Order&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveRecord&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_items&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:inverse_of&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payments&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:inverse_of&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipments&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:inverse_of&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  has_many &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:credits&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:inverse_of&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  belongs_to &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:billing_address&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:class_name&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Piggybak::Address&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  belongs_to &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipping_address&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:class_name&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Piggybak::Address&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  belongs_to &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  accepts_nested_attributes_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:billing_address&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:allow_destroy&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  accepts_nested_attributes_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipping_address&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:allow_destroy&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  accepts_nested_attributes_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipments&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:allow_destroy&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  accepts_nested_attributes_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_items&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:allow_destroy&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  accepts_nested_attributes_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;An order has many line items, payments, shipments and credits. It belongs to [one] billing and [one] shipping address. It can accept nested attributes for the billing address, shipping address, multiple shipments, line items, and payments. It cannot destroy payments (they can only be marked as refunded). In terms of using ActiveRecord callbacks for an order save, this means that all the nested attributes will also be validated during the save. Validation fails if any nested model data is not valid.&lt;/p&gt;
&lt;h3 id=&#34;step-1-user-enters-data-and-clicks-submit&#34;&gt;Step #1: user enters data, and clicks submit&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;
&lt;a href=&#34;/blog/2012/01/activerecord-callbacks-ecommerce-order/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/01/activerecord-callbacks-ecommerce-order/image-0.jpeg&#34; width=&#34;750&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3 id=&#34;step-2-before_validation&#34;&gt;Step #2: before_validation&lt;/h3&gt;
&lt;p&gt;Using a before_validation ActiveRecord callback, a few things happen on the order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some order defaults are set&lt;/li&gt;
&lt;li&gt;The order total is reset&lt;/li&gt;
&lt;li&gt;The order total due is reset&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;step-3-validation&#34;&gt;Step #3: validation&lt;/h3&gt;
&lt;p&gt;This happens without a callback. This method will execute validation on both order attributes (email, phone) and nested element attributes (address fields, shipment information, payment information, line_item information).&lt;/p&gt;
&lt;p&gt;Payments have a special validation step here. A custom validation method on the payment attributes is performed to confirm validity of the credit card:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;validates_each &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:payment_method_id&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |record, &lt;span style=&#34;color:#080&#34;&gt;attr&lt;/span&gt;, value|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; record.new_record?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    credit_card = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveMerchant&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Billing&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CreditCard&lt;/span&gt;.new(record.credit_card)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; !credit_card.valid?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      credit_card.errors.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key, value|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; value.any? &amp;amp;&amp;amp; ![&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;first_name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;last_name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;].include?(key)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          record.errors.add key, value
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This bit of code uses ActiveMerchant’s functionality to avoid reproducing business logic for credit card validation. The errors are added on the payment attributes (e.g. card_number, verification_code, expiration date) and presented to the user.&lt;/p&gt;
&lt;h3 id=&#34;step-4-after_validation&#34;&gt;Step #4: after_validation&lt;/h3&gt;
&lt;p&gt;Next, the after_validation callback is used to update totals. It does a few things here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Calculates shipping costs for new shipments only.&lt;/li&gt;
&lt;li&gt;Calculates tax charge on the order.&lt;/li&gt;
&lt;li&gt;Subtracts credits on the order, if they exist.&lt;/li&gt;
&lt;li&gt;Calculates total_due, to be used by payment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these calculations could be performed before_validation, after_validation is a bit more performance-friendly since tax and shipping calculations could in theory be expensive (e.g. shipping calculations could require calling an external API for real-time shipping lookup). These calculations are saved until after the order is confirmed to be valid.&lt;/p&gt;
&lt;h3 id=&#34;step-5-before_save-part-1&#34;&gt;Step #5: before_save part 1&lt;/h3&gt;
&lt;p&gt;Next, a before_save callback handles payment (credit card) processing. This must happen after validation has passed, and it can not happen after the order has saved because the user must be notified if it fails. If any before_save method returns false, the entire transaction fails. So in this case, after all validation has passed, and before the order saves, the payment must process successfully.&lt;/p&gt;
&lt;p&gt;Examples of failures here include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Credit card transaction denied for a number of reasons&lt;/li&gt;
&lt;li&gt;Payment gateway down&lt;/li&gt;
&lt;li&gt;Payment gateway API information incorrect&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;step-6-before_save-part-2&#34;&gt;Step #6: before_save part 2&lt;/h3&gt;
&lt;p&gt;After the payment processes, another before_save method is called to update the status of the order based on the totals paid. I initially tried placing this in an after_save method, but you tend to experience infinite loops if you try to save inside and after_save callback :)&lt;/p&gt;
&lt;h3 id=&#34;step-7-save&#34;&gt;Step #7: Save&lt;/h3&gt;
&lt;p&gt;Finally, if everything’s gone through, the order is saved.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;As I mentioned above, the RailsAdmin controllers were not extended or overridden to handle backroom order processing. All of the order processing is represented in the Order model in these active record callbacks. This also allows for the frontend order processing controller to be fairly lightweight, which is a standard practice for writing clean MVC code.&lt;/p&gt;
&lt;p&gt;Check out the full list of ActiveRecord callbacks &lt;a href=&#34;https://web.archive.org/web/20120125101409/http://edgeguides.rubyonrails.org/active_record_validations_callbacks.html&#34;&gt;here&lt;/a&gt;. And check out the Order model for Piggybak &lt;a href=&#34;https://github.com/piggybak/piggybak/blob/master/app/models/piggybak/order.rb&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Introducing Piggybak: A Mountable Ruby on Rails Ecommerce Engine</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/"/>
      <id>https://www.endpointdev.com/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/</id>
      <published>2012-01-06T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Here at End Point, we work with a variety of open source solutions, both full-fledged ecommerce applications and smaller modular tools. In our work with open source ecommerce applications, we spend a significant amount of time building out custom features, whether that means developing custom shipping calculations, product personalization, accounting integration or custom inventory management.&lt;/p&gt;
&lt;p&gt;There are advantages to working with a full-featured ecommerce platform. If you are starting from scratch, a full-featured platform can be a tool to quickly get product out the door and money in your pocket. But to do this, you must accept the assumptions that the application makes. And most generic, monolithic ecommerce platforms are created to satisfy many users.&lt;/p&gt;
&lt;p&gt;Working with a large monolithic ecommerce platform has disadvantages, too:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sometimes over-generic-izing a platform to satisfy the needs of many comes at the cost of code complexity, performance, or difficulty in customization.&lt;/li&gt;
&lt;li&gt;Occasionally, the marketing of an ecommerce solution overpromises and underdelivers, leaving users with unrealistic expectations on what they get out of the box.&lt;/li&gt;
&lt;li&gt;Customization on any of these platforms is not always simple, elegant, or cheap. And it may prevent easy maintenance or upgrades in the future. For example, building out customization A, B, and C on the platform &lt;em&gt;today&lt;/em&gt; may make it difficult to upgrade &lt;em&gt;later&lt;/em&gt; to use the added features X, Y, and Z.&lt;/li&gt;
&lt;li&gt;Users of a platform rely heavily on the maintainers of the project. This can mean that users may not be able to keep up with a fast-moving platform, or even that a slow-moving platform doesn’t stay up to date with ecommerce trends.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Full-featured ecommerce platforms do make sense for a some clients. Hosted ecommerce solutions (&lt;a href=&#34;http://www.shopify.com/&#34;&gt;Shopify&lt;/a&gt;, &lt;a href=&#34;http://www.volusion.com/&#34;&gt;Volusion&lt;/a&gt;) even make sense for a lot of people too. Here at End Point, we try to be realistic about the pros and cons of building off of an ecommerce platform, but balance that with available tools to develop something for our clients efficiently and pragmatically.&lt;/p&gt;
&lt;h3 id=&#34;enter-rails&#34;&gt;Enter Rails&lt;/h3&gt;
&lt;p&gt;I like Ruby, and Ruby on Rails a lot. With &lt;a href=&#34;http://gembundler.com/&#34;&gt;bundler&lt;/a&gt;, Rails has gotten particularly smart regarding managing dependencies. The Rails community also has a lot of great tools (gems) that can supplement an application. And a great MVC foundation plus include an improved way to manage assets (CSS, JavaScript, images) to help with performance and code organization. A few really great gems I’ve worked with are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://activemerchant.org/&#34;&gt;ActiveMerchant&lt;/a&gt;: a Ruby library for handling payments&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt;: admin interface for managing data&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mbleigh/acts-as-taggable-on&#34;&gt;ActsAsTaggableOn&lt;/a&gt;: tagging functionality&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://prawnpdf.org/api-docs/2.0/&#34;&gt;Prawn&lt;/a&gt;: a Ruby pdf generator&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/thoughtbot/paperclip&#34;&gt;Paperclip&lt;/a&gt;, file attachment management functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The other thing that’s really cool about Ruby on Rails is &lt;a href=&#34;http://edgeapi.rubyonrails.org/classes/Rails/Engine.html&#34;&gt;Rails Engines&lt;/a&gt;. Engines allow you to wrap Rails applications into modular elements that can be and easily shared across applications. And the parent application has control over the mount point of an Engine, meaning that a parent application can mount another engine at &amp;ldquo;/blog&amp;rdquo;, &amp;ldquo;/shop&amp;rdquo;, &amp;ldquo;/foo&amp;rdquo;, &amp;ldquo;/bar&amp;rdquo; without affecting the Engine’s behavior.&lt;/p&gt;
&lt;p&gt;I’m not trying to be a Rails fangirl here (I use WordPress for my personal website and still sometimes think Perl is the best tool for a job) :) But the ability to include and manage modular elements (gems, gems that are Engines) is great for building applications based on a few core modular elements.&lt;/p&gt;
&lt;h3 id=&#34;my-story&#34;&gt;My Story&lt;/h3&gt;
&lt;p&gt;Several weeks ago, I started putting together a prototype for a client for a Ruby on Rails ecommerce site. Their site has fairly custom needs with a complex data relationships (data model), complex search requirements, but relatively simple cart and checkout needs. I was certain that existing open source Ruby on Rails ecommerce platforms would require a significant amount of customization and would be a hassle to maintain. I also recently spent a good deal of time working with RailsAdmin on a non-ecommerce Rails project (&lt;a href=&#34;/blog/2011/08/railsadmin-gem-ecommerce/&#34;&gt;blogged here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Rather than try to fight against an existing framework, I developed a prototype website using RailsAdmin. My prototype quickly developed into an ecommerce Ruby on Rails Engine, which offers basic shopping cart functionality, but it doesn’t try to solve every problem for everyone. Below are some details about this gem as a Rails Engine, called &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;:&lt;/p&gt;
&lt;h3 id=&#34;non-tech-details&#34;&gt;Non-Tech Details&lt;/h3&gt;
&lt;p&gt;The Piggybak gem includes the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic shopping cart functionality for adding, updating, or removing items in a cart&lt;/li&gt;
&lt;li&gt;One page checkout, with AJAX for shipping and tax calculation&lt;/li&gt;
&lt;li&gt;Checkout for registered users or guests&lt;/li&gt;
&lt;li&gt;Configurable payment methods (via ActiveMerchant)&lt;/li&gt;
&lt;li&gt;Configurable shipping and tax calculators&lt;/li&gt;
&lt;li&gt;Admin interface for entering and managing orders&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are a few screenshots:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-0.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Demo homepage. The primary Rails application includes all the code for defining product navigation. In this case, it displays featured products and product categories.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-1-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One page checkout&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-2-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-2.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Admin dashboard: Note that distinction between the primary application and the Piggybak gem regarding left navigation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-3-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;350&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-3.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Any item in the application can become sellable. This nested form displays in the admin for the sellable items.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-4-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-4.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The cart form added to the image page is driven by the Piggybak gem, but the rest of the page content is driven by the primary application. The gem doesn’t make any decisions about what will be displayed on a standard product page. It only helps with generating the form to add something to the shopping cart.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-5.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left:1em; margin-right:1em&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-5.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The admin interface for editing an order. An order has one billing and shipping address. It can have many line items, shipments, and payments. All of these nested elements are controlled directly on the order edit page.&lt;/p&gt;
&lt;h3 id=&#34;tech-details&#34;&gt;Tech Details&lt;/h3&gt;
&lt;p&gt;The Piggybak gem has the following dependencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ruby on Rails 3.1+&lt;/li&gt;
&lt;li&gt;RailsAdmin&lt;/li&gt;
&lt;li&gt;Devise (which is a dependency of RailsAdmin)&lt;/li&gt;
&lt;li&gt;CanCan (the parent application takes responsibility for defining authorization rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is certainly not a short list of dependencies, but if you are developing a new application on Rails, you are likely already using a couple of these tools. And I highly recommend using RailsAdmin :)&lt;/p&gt;
&lt;p&gt;To get an idea of how the mountable solution works, these are the installation steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add to Gemfile and install with bundler&lt;/li&gt;
&lt;li&gt;Rake task for copying migrations and run migrations&lt;/li&gt;
&lt;li&gt;Mount engine in the parent’s appplication config/routes.rb&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then the following integration points are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;acts_as_variant&lt;/strong&gt; is added inside any model in your application to become sellable. This affectively assigns a relationship between your model(s) and the variants table. A variant belongs_to an item through a polymorphic relationship, and a model has_one variant. The variants table has information for its sku, price, quantity on hand, and shopping cart display name.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;acts_as_orderer&lt;/strong&gt; is added inside inside the user model in your application that owns orders (probably User).&lt;/li&gt;
&lt;li&gt;&amp;lt;%= cart_form(@item) %&amp;gt; is a helper method that displays an add to cart form&lt;/li&gt;
&lt;li&gt;&amp;lt;%= cart_link %&amp;gt; is a helper method that displays a link to the cart with the current number of items and total&lt;/li&gt;
&lt;li&gt;&amp;lt;%= orders_link(&amp;ldquo;Order History&amp;rdquo;) %&amp;gt; is a helper method which links to a users orders page&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Here at End Point, we’ve had a few internal discussions about building ecommerce solutions based on more modular components to address the disadvantages we’ve seen in working with large monolithic ecommerce platforms. Rails provides a strong base for stitching modular components together easily.&lt;/p&gt;
&lt;div style=&#34;margin: 0 25px 0 0; float:left;&#34;&gt;&lt;img style=&#34;margin: 0;&#34; border=&#34;0&#34; src=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/image-6.jpeg&#34; width=&#34;290&#34;/&gt;&lt;br/&gt;&lt;small&gt;© Steph Skardal&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;My goal with this tool was to write a modular cart and checkout component that takes advantage of RailsAdmin’s DSL to provide a nice Admin interface for nested forms for models that already existing in your application. It wasn’t created to solve every ecommerce problem, or world peace. As its name indicates, this gem &lt;b&gt;piggybak&lt;/b&gt;’s off of an existing Rails application.&lt;/p&gt;
&lt;p&gt;It leaves the decision of all product finding methods and product navigation up to the developer, which means it might be great for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A deal of the day site that controls items offered daily. Deal of the day sites often don’t fit the standard mold of ecommerce so ecommerce platforms don’t always suit them well. And they may need significant performance customization, which is not always a feature included in a generic ecommerce platform.&lt;/li&gt;
&lt;li&gt;An ecommerce site with complex demands for search, where existing ecommerce solutions aren’t easily integrated with those search solutions. A developer may build their own custom search solution and mount piggybak for handling cart and checkout only.&lt;/li&gt;
&lt;li&gt;An ecommerce site with a complex data model, where multiple types of items with varied navigation. This gem gives the functionality to turn any item on a site into a sellable item (or variant).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Demo and more documentation is forthcoming. View the repository &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;here&lt;/a&gt;. Contributions (see TODO) appreciated. As I work through this project for the client in the next few weeks, I’m sure that there’ll be a few minor bugs to work out.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
