<?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/spree/</id>
  <link href="https://www.endpointdev.com/blog/tags/spree/"/>
  <link href="https://www.endpointdev.com/blog/tags/spree/" rel="self"/>
  <updated>2021-09-28T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Integrating the Estes Freight Shipping SOAP API as a Spree Shipping Calculator</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/09/estes-shipping-spree/"/>
      <id>https://www.endpointdev.com/blog/2021/09/estes-shipping-spree/</id>
      <published>2021-09-28T00:00:00+00:00</published>
      <author>
        <name>Patrick Lewis</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/09/estes-shipping-spree/20190617-084228-small.jpg&#34; alt=&#34;Cargo ship on sea with dark clouds&#34;&gt;&lt;/p&gt;
&lt;!-- photo by Jon Jensen --&gt;
&lt;p&gt;One of our clients with a Spree-based e-commerce site was interested in providing automated shipping quotes to their customers using their freight carrier &lt;a href=&#34;https://www.estes-express.com/&#34;&gt;Estes&lt;/a&gt;. After doing some research I found that Estes provided a variety of SOAP APIs and determined a method for extending Spree with custom shipping rate calculators. This presented an interesting challenge to me on several levels: most of my previous API integration experience was with &lt;a href=&#34;https://en.wikipedia.org/wiki/Representational_state_transfer&#34;&gt;REST&lt;/a&gt;, not &lt;a href=&#34;https://en.wikipedia.org/wiki/SOAP&#34;&gt;SOAP&lt;/a&gt; APIs, and I had not previously worked on custom shipping calculators for Spree. Fortunately, the Estes SOAP API documentation and some code examples of other Spree shipping calculators were all I needed to create a successful integration of the freight shipping API for this client.&lt;/p&gt;
&lt;h3 id=&#34;estes-api-documentation&#34;&gt;Estes API Documentation&lt;/h3&gt;
&lt;p&gt;The Estes &lt;a href=&#34;https://www.estes-express.com/resources/digital-services/api/rate-quote-web-service-v4-0&#34;&gt;Rate Quote Web Service&lt;/a&gt; API is the one that I relied on for being able to generate shipping quotes based on a combination of source address, destination address, and package weight. I found the developer documentation to be thorough and helpful, and was able to create working client code to send a request and receive a response relatively quickly. Many optional fields can be provided when making requests but I found that I only needed to use a small subset of these, as shown in the example code below.&lt;/p&gt;
&lt;p&gt;The one aspect of the API that tripped me up a bit was their use of &lt;code&gt;CN&lt;/code&gt; as the country code for Canada; Spree and most other codebases I have encountered use the international standard &lt;a href=&#34;https://www.iso.org/iso-3166-country-codes.html&#34;&gt;ISO 3166 country codes&lt;/a&gt; with &lt;code&gt;CA&lt;/code&gt; for Canada, so I had to add a small workaround for that in my client code when requesting shipping quotes to Canadian addresses. Another limitation I encountered is that the API expected to receive only 5-digit US and 6-character Canadian postal codes, so I had to do a bit of manipulation in my shipping calculator to account for that.&lt;/p&gt;
&lt;h3 id=&#34;ruby-soap-client&#34;&gt;Ruby SOAP Client&lt;/h3&gt;
&lt;p&gt;I researched Ruby SOAP clients and soon found &lt;a href=&#34;https://www.savonrb.com/&#34;&gt;Savon&lt;/a&gt;, the &amp;ldquo;Heavy metal SOAP client&amp;rdquo;, which proved to be very easy to integrate into my existing Rails/​Spree application. I added the savon gem to my project&amp;rsquo;s Gemfile and I was quickly able to instantiate a Savon client and configure it using the WSDL provided by Estes. After that, most of the integration work involved crafting a valid XML payload for my request and then parsing the response.&lt;/p&gt;
&lt;h3 id=&#34;spree-shipping-calculators&#34;&gt;Spree Shipping Calculators&lt;/h3&gt;
&lt;p&gt;The final piece of the puzzle was implementing the Savon client into a Spree shipping calculator class. This process allowed me to retrieve details about the current user&amp;rsquo;s order and then return the calculated shipping estimates within the context of the Spree checkout pages. Looking at existing shipping calculator code helped set me on the right path here; in the end, it was just a matter of defining a new class that inherited from the base &lt;code&gt;Spree::ShippingCalculator&lt;/code&gt; class and then defining the &lt;code&gt;#compute_package&lt;/code&gt; method expected by Spree for returning the shipping cost of a given package.&lt;/p&gt;
&lt;h3 id=&#34;code-example&#34;&gt;Code Example&lt;/h3&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;# app/models/spree/calculator/shipping/estes_calculator.rb&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;Spree::Calculator::Shipping&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;# Custom freight shipping rate API integration&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;EstesCalculator&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ShippingCalculator&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;ESTES_API_URL&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://www.estes-express.com/tools/rating/ratequote/v4.0/services/RateQuoteService?wsdl&amp;#39;&lt;/span&gt;.freeze
&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:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;description&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:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Estes Freight&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;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;compute_package&lt;/span&gt;(package)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      country_code = package.order.ship_address.country.iso
&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;return&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; country_code.in?([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;US&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;CA&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MX&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;      client = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Savon&lt;/span&gt;.client(
&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;filters&lt;/span&gt;: %i[user password account],
&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;log&lt;/span&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;log_level&lt;/span&gt;: &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:debug&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;logger&lt;/span&gt;: &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Logger&lt;/span&gt;.new(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;.root.join(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;savon.log&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:#a60;background-color:#fff0f0&#34;&gt;pretty_print_xml&lt;/span&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;wsdl&lt;/span&gt;: &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ESTES_API_URL&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;      country_code = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;CN&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; country_code == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;CA&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;# Estes uses CN for Canada&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      postal_code = package.order.ship_address.zipcode
&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;      postal_code =
&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; country_code == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;CN&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          postal_code.delete(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;).first(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&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;          postal_code.first(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&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;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-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      xml = &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;~XML
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;soapenv:Envelope&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:#369&#34;&gt;xmlns:soapenv=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&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:#369&#34;&gt;xmlns:rat=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://ws.estesexpress.com/ratequote&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:#369&#34;&gt;xmlns:rat1=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://ws.estesexpress.com/schema/2019/01/ratequote&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;soapenv:Header&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat:auth&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat:user&amp;gt;&lt;/span&gt;#{ENV[&amp;#39;ESTES_USER&amp;#39;]}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat:user&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat:password&amp;gt;&lt;/span&gt;#{ENV[&amp;#39;ESTES_PASSWORD&amp;#39;]}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat:password&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat:auth&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/soapenv:Header&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;soapenv:Body&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:rateRequest&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:requestID&amp;gt;&lt;/span&gt;#{package.order.number}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:requestID&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:account&amp;gt;&lt;/span&gt;#{ENV[&amp;#39;ESTES_ACCOUNT&amp;#39;]}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:account&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:originPoint&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:countryCode&amp;gt;&lt;/span&gt;US&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:countryCode&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:postalCode&amp;gt;&lt;/span&gt;10001&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:postalCode&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:originPoint&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:destinationPoint&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:countryCode&amp;gt;&lt;/span&gt;#{country_code}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:countryCode&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:postalCode&amp;gt;&lt;/span&gt;#{postal_code}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:postalCode&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:destinationPoint&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:payor&amp;gt;&lt;/span&gt;S&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:payor&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:terms&amp;gt;&lt;/span&gt;C&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:terms&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:baseCommodities&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:commodity&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:class&amp;gt;&lt;/span&gt;50&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:class&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;rat1:weight&amp;gt;&lt;/span&gt;#{package_weight(package)}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:weight&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:commodity&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:baseCommodities&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/rat1:rateRequest&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/soapenv:Body&amp;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:#b06;font-weight:bold&#34;&gt;&amp;lt;/soapenv:Envelope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      XML&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;      response = client.call(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:get_quote&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;xml&lt;/span&gt;: xml)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      quotes = response.body.dig(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:rate_quote&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:quote_info&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:quote&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; quotes.is_a?(&lt;span style=&#34;color:#038&#34;&gt;Array&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        quotes.first.dig(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:pricing&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:total_price&lt;/span&gt;).to_f
&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;elsif&lt;/span&gt; quotes.is_a?(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Hash&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        quotes.dig(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:pricing&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:total_price&lt;/span&gt;).to_f
&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;        &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&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;rescue&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Savon&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Error&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;# Record shipping rate as 0 if an API error is caught, 0 amount will indicate need to show user an error message on the shipping rate page&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:#00d;font-weight:bold&#34;&gt;0&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:#080&#34;&gt;private&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;package_weight&lt;/span&gt;(package)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      weight = package.contents.sum { |content| content.line_item.weight }
&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; weight &amp;lt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;# enforce minimum package weight&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:#00d;font-weight:bold&#34;&gt;5&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;        weight.round
&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;I was pleased that I was able to quickly build a custom shipping calculator in Spree that used the Estes Rate Quote API to provide accurate shipping estimates for large freight packages. The high quality documentation of the Estes API and the Savon SOAP Client gem made for a pleasant development experience, and the client was happy to gain the new functionality for their Spree store.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>wroc_love.rb 2017 part 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/03/wrocloverb-2017-part-1/"/>
      <id>https://www.endpointdev.com/blog/2017/03/wrocloverb-2017-part-1/</id>
      <published>2017-03-18T00:00:00+00:00</published>
      <author>
        <name>Wojtek Ziniewicz</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;https://wrocloverb.com/&#34;&gt;wroc_love.rb&lt;/a&gt; is a single-track 3-day conference that takes place in Wrocław, Poland, every year in March.&lt;/p&gt;
&lt;p&gt;Here’s a subjective list of most interesting talks from the first day:&lt;/p&gt;
&lt;h3 id=&#34;kafka--karafka-by-maciej-mensfeld&#34;&gt;Kafka / Karafka by Maciej Mensfeld&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/karafka/karafka&#34;&gt;Karafka&lt;/a&gt; is another library that simplifies Apache Kafka usage in Ruby. It lets Ruby on Rails apps benefit from horizontally scalable message busses in a pub-sub (or publisher/​consumer) type of network.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;a href=&#34;https://kafka.apache.org/&#34;&gt;Kafka&lt;/a&gt; is (&lt;em&gt;probably&lt;/em&gt;) better message/​task broker for your app:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;broadcasting is a real power feature of Kafka (HTTP lacks that)&lt;/li&gt;
&lt;li&gt;author claims that it’s easier to support than ZeroMQ/​RabbitMQ&lt;/li&gt;
&lt;li&gt;it’s namespaced with topics (similar to ROS, the &lt;a href=&#34;http://www.ros.org/&#34;&gt;Robot Operating System&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;great replacement for &lt;a href=&#34;https://github.com/zendesk/ruby-kafka&#34;&gt;ruby-kafka&lt;/a&gt; and &lt;a href=&#34;https://github.com/bpot/poseidon&#34;&gt;Poseidon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Karafka &lt;a href=&#34;https://t.co/g9LQZiAV4i&#34;&gt;https://t.co/g9LQZiAV4i&lt;/a&gt; microframework to have &lt;a href=&#34;https://twitter.com/hashtag/rails?src=hash&#34;&gt;#rails&lt;/a&gt;-like development performance with &lt;a href=&#34;https://twitter.com/hashtag/kafka?src=hash&#34;&gt;#kafka&lt;/a&gt; in &lt;a href=&#34;https://twitter.com/hashtag/ruby?src=hash&#34;&gt;#ruby&lt;/a&gt; &lt;a href=&#34;https://twitter.com/maciejmensfeld&#34;&gt;@maciejmensfeld&lt;/a&gt; &lt;a href=&#34;https://twitter.com/hashtag/wrocloverb?src=hash&#34;&gt;#wrocloverb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— Maciek Rząsa (@mjrzasa) &lt;a href=&#34;https://twitter.com/mjrzasa/status/842771868239192064&#34;&gt;17 marzo 2017&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;machine-learning-to-the-rescue-by-mariusz-gil&#34;&gt;Machine Learning to the Rescue by Mariusz Gil&lt;/h3&gt;
&lt;p&gt;This talk was devoted to Machine Learning success (and failure) story of the author.&lt;/p&gt;
&lt;p&gt;Author underlined that Machine Learning is a &lt;strong&gt;process&lt;/strong&gt; and proposed following &lt;strong&gt;workflow&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;define a problem&lt;/li&gt;
&lt;li&gt;gather your data&lt;/li&gt;
&lt;li&gt;understand your data&lt;/li&gt;
&lt;li&gt;prepare and condition the data&lt;/li&gt;
&lt;li&gt;select &amp;amp; run your algorithms&lt;/li&gt;
&lt;li&gt;tune algorithms parameters&lt;/li&gt;
&lt;li&gt;select final model&lt;/li&gt;
&lt;li&gt;validate final model (test using production data)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Mariusz described few ML problems that he has dealt with in the past. One of them was a project designed to estimate cost of a code review. He outlined the process of tuning the input data. Here’s a list of what comprised the input for a code review estimation cost:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;number of lines changed&lt;/li&gt;
&lt;li&gt;number of files changed&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Efferent_coupling&#34;&gt;efferent&lt;/a&gt; coupling&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Coupling_(computer_programming)&#34;&gt;afferent&lt;/a&gt; coupling&lt;/li&gt;
&lt;li&gt;number of classes&lt;/li&gt;
&lt;li&gt;number of interfaces&lt;/li&gt;
&lt;li&gt;inheritance level&lt;/li&gt;
&lt;li&gt;number of method calls&lt;/li&gt;
&lt;li&gt;LLOC metric (Logical Lines of Code, excluding empty or comment lines)&lt;/li&gt;
&lt;li&gt;LCOM metric (Lack of Cohesion between Methods—​whether single responsibility pattern is followed or not)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;spree-lightning-talk-by-sparksolutionsco&#34;&gt;Spree lightning talk by &lt;a href=&#34;https://sparksolutions.co/&#34;&gt;sparksolutions.co&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the lightning talks was devoted to Spree. Here’s some interesting latest data from the Spree world:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;number of contributors to Spree: 700&lt;/li&gt;
&lt;li&gt;it’s very modular&lt;/li&gt;
&lt;li&gt;it’s API driven&lt;/li&gt;
&lt;li&gt;it’s one of the biggest repos on GitHub&lt;/li&gt;
&lt;li&gt;very large number of extensions&lt;/li&gt;
&lt;li&gt;it drives thousands of stores worldwide&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://sparksolutions.co/&#34;&gt;Spark Solutions&lt;/a&gt; is a maintainer&lt;/li&gt;
&lt;li&gt;Popular companies that use Spree: GoDaddy, Goop, Casper, Bonobos, Littlebits, Greetabl&lt;/li&gt;
&lt;li&gt;it support Rails 5, Rails 4.2 and Rails 3.x&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Author also released newest 3.2.0 stable version during the talk:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;releasing spree 3.2.0 live during lightning talk &lt;a href=&#34;https://twitter.com/hashtag/wrocloverb?src=hash&#34;&gt;#wrocloverb&lt;/a&gt; &lt;a href=&#34;https://t.co/9oPcB5CTfB&#34;&gt;pic.twitter.com/9oPcB5CTfB&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— Wojciech Ziniewicz (@fribulusxax) &lt;a href=&#34;https://twitter.com/fribulusxax/status/842800094915301376&#34;&gt;17 marzo 2017&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;

      </content>
    </entry>
  
    <entry>
      <title>The merchant login ID or password is invalid or the account is inactive, and to how to fix it in Spree</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/06/the-merchant-login-id-or-password-is/"/>
      <id>https://www.endpointdev.com/blog/2016/06/the-merchant-login-id-or-password-is/</id>
      <published>2016-06-02T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;Authorize.net has disabled the RC4 cipher suite on their test server. Their production server update will follow soon. So, in order to ensure your, or your client’s, site(s) do not experience any interruption in payment processing it is wise to place a test order in the Authorize.net test environment.&lt;/p&gt;
&lt;p&gt;The projects I was testing were all &lt;a href=&#34;https://spreecommerce.org/&#34;&gt;Spree&lt;/a&gt; Gem (2.1.x). The Spree Gem uses the &lt;a href=&#34;https://github.com/activemerchant/active_merchant&#34;&gt;ActiveMerchant&lt;/a&gt;Gem (in Spree 2.1.x it’s ActiveMerchant version 1.34.x). Spree allows you to sign into the admin and select which server your Authorize.net payment method will hit- production or test. There is another option for selecting a “Test &lt;em&gt;Mode&lt;/em&gt;” transaction. The difference between a test &lt;strong&gt;server&lt;/strong&gt; transaction and a test &lt;strong&gt;mode&lt;/strong&gt; transaction is explained quite succinctly on the &lt;a href=&#34;http://developer.authorize.net/hello_world/testing_guide/&#34;&gt;Authorize.net documentation&lt;/a&gt;. To summarize it, test &lt;strong&gt;server&lt;/strong&gt; transactions are never sent to financial institutions for processing but are stored in Authorize.net (so you can see their details). Transactions in test &lt;strong&gt;mode&lt;/strong&gt; however are not stored and return a transaction ID of zero.&lt;/p&gt;
&lt;p&gt;I wanted to use my Authorize.net test account to ensure my clients were ready for the RC4 cipher suite disablement. I ran across a few strange things. First, for three sites, no matter what I did, I kept getting errors saying my Authorize.net account was either inactive or I was providing the wrong credentials. I signed in to Authorize.net and verified my account was active. I triple checked the credentials, they were right. So, I re-read the Spree docs thinking that perhaps I needed to use a special word or format to actually use the test server (“test” versus “Test” or something like that).&lt;/p&gt;
&lt;p&gt;Below is a screenshot of the test payment method I had created and was trying to use.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/06/the-merchant-login-id-or-password-is/image-0.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/2016/06/the-merchant-login-id-or-password-is/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Since I kept getting errors I looked through the Spree code, then the &lt;a href=&#34;https://github.com/activemerchant/active_merchant&#34;&gt;ActiveMerchant Gem&lt;/a&gt; that Spree is using.&lt;/p&gt;
&lt;p&gt;Below, you can see that the ActiveMerchant is deciding which URL to use (test or live) based on the value of &lt;strong&gt;test?&lt;/strong&gt; (line 15).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateways/authorize_net.rb&#34;&gt;active_merchant/lib/active_merchant/billing/gateways/authorize_net.rb&lt;/a&gt;&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;nokogiri&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;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ActiveMerchant&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;#:nodoc:&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;Billing&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;#:nodoc:&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AuthorizeNetGateway&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Gateway&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;include&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Empty&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;self&lt;/span&gt;.test_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://apitest.authorize.net/xml/v1/request.api&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;self&lt;/span&gt;.live_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://api2.authorize.net/xml/v1/request.api&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:#888&#34;&gt;# etc.&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;url&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;test&lt;/span&gt;? ? test_url : live_url
&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;How and where is this set? Spree passes the ActiveMerchant Gem some data which the ActiveMerchant Gem uses to create Response objects. Below is the code where ActiveMerchant handles this data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/response.rb&#34;&gt;active_merchant/lib/active_merchant/billing/response.rb&lt;/a&gt;&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;ActiveMerchant&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;#:nodoc:&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;Billing&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;#:nodoc:&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Error&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveMerchantError&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;#:nodoc:&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:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Response&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;attr_reader&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:params&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:message&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:authorization&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:avs_result&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:cvv_result&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:error_code&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:emv_authorization&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;# etc.&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;test?&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:#33b&#34;&gt;@test&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize&lt;/span&gt;(success, message, params = {}, options = {})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#33b&#34;&gt;@success&lt;/span&gt;, &lt;span style=&#34;color:#33b&#34;&gt;@message&lt;/span&gt;, &lt;span style=&#34;color:#33b&#34;&gt;@params&lt;/span&gt; = success, message, params.stringify_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#33b&#34;&gt;@test&lt;/span&gt; = options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&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 style=&#34;color:#33b&#34;&gt;@authorization&lt;/span&gt; = options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:authorization&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:#33b&#34;&gt;@fraud_review&lt;/span&gt; = options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:fraud_review&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:#33b&#34;&gt;@error_code&lt;/span&gt; = options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:error_code&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:#33b&#34;&gt;@emv_authorization&lt;/span&gt; = options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:emv_authorization&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:#33b&#34;&gt;@avs_result&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:avs_result&lt;/span&gt;].kind_of?(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AVSResult&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:avs_result&lt;/span&gt;].to_hash
&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;          &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AVSResult&lt;/span&gt;.new(options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:avs_result&lt;/span&gt;]).to_hash
&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:#33b&#34;&gt;@cvv_result&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:cvv_result&lt;/span&gt;].kind_of?(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CVVResult&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:cvv_result&lt;/span&gt;].to_hash
&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;          &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CVVResult&lt;/span&gt;.new(options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:cvv_result&lt;/span&gt;]).to_hash
&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;&lt;a href=&#34;https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateway.rb&#34;&gt;active_merchant/lib/active_merchant/billing/gateway.rb&lt;/a&gt;&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;# Are we running in test mode?&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;test?&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:#33b&#34;&gt;@options&lt;/span&gt;.has_key?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;) ? &lt;span style=&#34;color:#33b&#34;&gt;@options&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;] : &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;.test?)
&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;Now that I was more familiar with ActiveMerchant, I wanted to verify that Spree was passing the data as intended&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/06/the-merchant-login-id-or-password-is/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/2016/06/the-merchant-login-id-or-password-is/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/06/the-merchant-login-id-or-password-is/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/2016/06/the-merchant-login-id-or-password-is/image-2.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;I could see in &lt;a href=&#34;https://github.com/spree/spree/blob/8bfa0824d2a7f6cfa6efc9bd4e32d1d564f6270b/core/app/models/spree/gateway.rb&#34;&gt;spree/core/app/models/spree/gateway.rb&lt;/a&gt; that Spree was setting ActiveMerchant::Billing::Base.gateway_mode equal to the server param as a symbol. I verified it with some logging.&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;provider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	gateway_options = options
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	gateway_options.delete &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:login&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; gateway_options.has_key?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:login&lt;/span&gt;) &lt;span style=&#34;color:#080&#34;&gt;and&lt;/span&gt; gateway_options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:login&lt;/span&gt;].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;if&lt;/span&gt; gateway_options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:server&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;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;Base&lt;/span&gt;.gateway_mode = gateway_options[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:server&lt;/span&gt;].to_sym
&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:#33b&#34;&gt;@provider&lt;/span&gt; ||= provider_class.new(gateway_options)
&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;At this point I was satisfied that Spree was sending a server param. I also knew Spree was setting Active Merchant’s Base.gateway_mode as intended. I then reviewed &lt;a href=&#34;https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateway.rb&#34;&gt;active_merchant/lib/active_merchant/billing/gateway.rb
&lt;/a&gt; once more&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;# Are we running in test mode?&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;test?&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:#33b&#34;&gt;@options&lt;/span&gt;.has_key?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;) ? &lt;span style=&#34;color:#33b&#34;&gt;@options&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;] : &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base&lt;/span&gt;.test?)
&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/activemerchant/active_merchant/blob/v1.34.0/lib/active_merchant/billing/base.rb&#34;&gt;active_merchant/lib/active_merchant/billing/base.rb&lt;/a&gt;&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:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;test?&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;.gateway_mode == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&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;So, that’s it! We know from the exceptions I raised that Spree is sending a test key and a test_mode key. They seem to be the same value but with different keys (I’m guessing that’s a mistake), and they both just seem to indicate if the test &lt;strong&gt;mode&lt;/strong&gt; checkbox was checked or not in the Spree admin. However, Base.test? is the &lt;strong&gt;server&lt;/strong&gt; selection and comes from whatever anyone enters in the server input box in the Spree admin. So, we just need to update the ternary operator to check if &lt;code&gt;@options[:test]&lt;/code&gt; (test &lt;em&gt;mode&lt;/em&gt;) &lt;strong&gt;or&lt;/strong&gt; &lt;code&gt;Base.test?&lt;/code&gt; (test &lt;em&gt;server&lt;/em&gt;) is true.&lt;/p&gt;
&lt;p&gt;Since this is Spree, I created a decorator to override the test? method.&lt;/p&gt;
&lt;p&gt;app/models/gateway_decorator.rb&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;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;Gateway&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;  &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;test?&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:#33b&#34;&gt;@options&lt;/span&gt;.has_key?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;) &amp;amp;&amp;amp; &lt;span style=&#34;color:#33b&#34;&gt;@options&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:test&lt;/span&gt;] || &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;Base&lt;/span&gt;.test?
&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;Lastly, I placed some test orders and it all worked as intended.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Authorize.net is disabling the RC4 cipher suite. If your site(s) uses that, your payment processing may be interrupted. Since the test environment has been updated by Authorize.net, you can see if your site(s) is compliant by posting test transactions to the test environment. If it works, then your site(s) should be compliant and ready when Authorize.net applies the changes to the production server.&lt;/p&gt;
&lt;p&gt;Spree 2.1.x (and perhaps all other Spree versions) &lt;strong&gt;always&lt;/strong&gt; send the test key, so the ActiveMerchant Gem will always just use the boolean value of that key instead of ever checking to see what the server was set to. Further, this fix makes things a little bit more robust in my opinion by checking if test mode OR the test server was specified, rather than &lt;strong&gt;only checking&lt;/strong&gt; which server (gateway_mode) was specified &lt;strong&gt;if the test key was absent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Alternatively, you could probably make Spree only pass the test key if the value was true.  Either way, if you are trying to send test orders to the test environment for a Spree site of at least some versions and have not implemented one of these changes, you will be unable to do so until you add a similar fix as I have described here. If you need any further assistance, please &lt;a href=&#34;/contact/&#34;&gt;reach out to us&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Admin pages unreachable (500 errors)</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/03/spree-admin-pages-unreachable-500-errors/"/>
      <id>https://www.endpointdev.com/blog/2016/03/spree-admin-pages-unreachable-500-errors/</id>
      <published>2016-03-17T00:00:00+00:00</published>
      <author>
        <name>Kent Krenrich</name>
      </author>
      <content type="html">
        &lt;p&gt;I was notified a few minutes ago by one of our Spree clients that their admin interface was unreachable due to errors.&lt;/p&gt;
&lt;p&gt;Digging into the logs, I discovered SocketErrors (DNS lookup failures) were behind the 500 errors. Digging deeper, I discovered the SocketErrors were coming from a Spree file attempting to access “alerts.spreecommerce.com”. I confirmed in my browser that alerts.spreecommerce.com fails to resolve.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/spree/spree/commit/d9bd19468d34ee12cc5ce0f73509748ca569957f&#34;&gt;This Git commit&lt;/a&gt; discusses the removal of the class, but if you haven’t stayed current and you’ve left the “Check for alerts” box checked, you may need to do some manual editing of your stored preferences to get the UI to load again.&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Preference&lt;/span&gt;.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;spree/app_configuration/check_for_spree_alerts&amp;#34;&lt;/span&gt;).first.update_attributes(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It does appear that your app will need to restart to pull in this change.&lt;/p&gt;
&lt;p&gt;I’m not sure what the chances are your particular config key might vary, so please use the above with caution.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Commerce “invalid value for Integer(): &#34;09&#34;” in Spree​::Checkout​/update</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/01/spree-commerce-invalid-value-for/"/>
      <id>https://www.endpointdev.com/blog/2015/01/spree-commerce-invalid-value-for/</id>
      <published>2015-01-14T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;Hello again all. I like to monitor the orders and exceptions of the Spree sites I work on to ensure everything is working as intended. One morning I noticed an unusual error: “invalid value for Integer(): &amp;quot;09&amp;quot;” in Spree::Checkout/update on a Spree 2.1.x site.&lt;/p&gt;
&lt;h3 id=&#34;the-issue&#34;&gt;The Issue&lt;/h3&gt;
&lt;p&gt;Given that this is a Spree-powered e-commerce site, a customer’s inability to checkout is quite alarming. In the backtrace I could see that a &lt;strong&gt;string&lt;/strong&gt; of “09” was causing an invalid value for an &lt;strong&gt;integer&lt;/strong&gt;. Why hadn’t I seen this on every order in that case?&lt;/p&gt;
&lt;p&gt;I went into the browser and completed some test orders. The bug seemed to affect only credit cards with a leading “0” in the expiration month, and then only certain expiration months. I returned to the backtrace and saw this error was occurring with Active Merchant. So, Spree was passing Active Merchant a string while Active Merchant was expecting an integer.&lt;/p&gt;
&lt;p&gt;Armed with a clearer understanding of the problem, I did some Googling. I came across &lt;a href=&#34;https://github.com/Shopify/active_merchant/issues/919&#34;&gt;this post&lt;/a&gt;. This post describes the source of this issue as being the behavior of sprintf which I will describe below. This &lt;a href=&#34;https://www.ruby-forum.com/topic/77946&#34;&gt;topic&lt;/a&gt; was discussed in the &lt;a href=&#34;https://www.ruby-forum.com&#34;&gt;Ruby Forum&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;octal-numbers&#34;&gt;Octal Numbers&lt;/h3&gt;
&lt;p&gt;As per Daniel Martin on the aforementioned post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sprintf(&amp;quot;%d&amp;quot;,&amp;lsquo;08&amp;rsquo;)   ==&amp;gt;  ArgumentError&lt;/li&gt;
&lt;li&gt;sprintf(&amp;quot;%d&amp;quot;,&amp;lsquo;8&amp;rsquo;)    ==&amp;gt;  &amp;ldquo;8&amp;rdquo;&lt;/li&gt;
&lt;li&gt;sprintf(&amp;quot;%d&amp;quot;,&amp;lsquo;08&amp;rsquo;.to_i) ==&amp;gt;  &amp;ldquo;8&amp;rdquo;&lt;/li&gt;
&lt;li&gt;sprintf(&amp;quot;%f&amp;quot;,&amp;lsquo;08&amp;rsquo;)   ==&amp;gt;  &amp;ldquo;8.000000&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, sprintf cannot convert ‘08’ or ‘09’ to a decimal. Matthias Reitlinger notes,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“%d tells sprintf to expect an Integer as the corresponding argument. Being given a String instead it tries to convert it by calling Kernel#Integer”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In the same post, we can review some documentation of Kernel#Integer&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/spree-commerce-invalid-value-for/image-0.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/2015/01/spree-commerce-invalid-value-for/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;We can see here that if the argument being provided is a string (and it is since that is what Spree is sending), the “0” will be honored. Again, we know&lt;/p&gt;
&lt;table&gt;
    &lt;tbody&gt;&lt;tr&gt;
      &lt;td&gt;sprintf(&#34;%d&#34;,&#39;01&#39;) =&gt; &#34;1&#34;&lt;/td&gt;
      &lt;td&gt; | &lt;/td&gt;
      &lt;td&gt;sprintf(&#34;%d&#34;, 01) =&gt; &#34;1&#34;&lt;/td&gt;
    &lt;/tr&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;02&#39;) =&amp;gt; &amp;quot;2&amp;quot;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 02) =&amp;gt; &amp;quot;2&amp;quot;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;03&#39;) =&amp;gt; &amp;quot;3&amp;quot;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 03) =&amp;gt; &amp;quot;3&amp;quot;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;04&#39;) =&amp;gt; &amp;quot;4&amp;quot;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 04) =&amp;gt; &amp;quot;4&amp;quot;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;05&#39;) =&amp;gt; &amp;quot;5&amp;quot;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 05) =&amp;gt; &amp;quot;5&amp;quot;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;06&#39;) =&amp;gt; &amp;quot;6&amp;quot;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 06) =&amp;gt; &amp;quot;6&amp;quot;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;07&#39;) =&amp;gt; &amp;quot;7&amp;quot;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 07) =&amp;gt; &amp;quot;7&amp;quot;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;08&#39;) =&amp;gt; error&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 08) =&amp;gt; error&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;,&#39;09&#39;) =&amp;gt; error&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;sprintf(&amp;quot;%d&amp;quot;, 09) =&amp;gt; error&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
  &lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;By pre-prepending the “0” to the numbers, they are being marked as ‘octal’. &lt;a href=&#34;https://en.wikipedia.org/wiki/Octal&#34;&gt;Wikipedia&lt;/a&gt; defines octal numbers as&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The octal numeral system, or oct for short, is the base-8 number system, and uses the digits 0 to 7. Octal numerals can be made from binary numerals by grouping consecutive binary digits into groups of three (starting from the right).”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So, 08 and 09 are not octal numbers.&lt;/p&gt;
&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;
&lt;p&gt;This is why this checkout error did not occur on every order whose payment expiration month had a leading “0”, only August (08) and September (09) were susceptible as the leading ‘0’ indicates we are passing in an octal of which 08 and 09 are not valid examples of. So, I made Spree send integers (sprintf(&amp;quot;%d&amp;quot;,8) #=&amp;gt; &amp;ldquo;8&amp;rdquo; and sprintf(&amp;quot;%d&amp;quot;,9) #=&amp;gt; &amp;ldquo;9&amp;rdquo;) so that the leading “0” would not get sent (thereby not trying to pass them as octals). I created a app/models/spree/credit_card_decorator.rb file with the contents&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CreditCard&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;  &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;expiry&lt;/span&gt;=(expiry)
&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; expiry.present?
&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;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:month&lt;/span&gt;], &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:year&lt;/span&gt;] = expiry.delete(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;).split(&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:#038&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:year&lt;/span&gt;] = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;20&amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:year&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;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:year&lt;/span&gt;].length == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&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;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:year&lt;/span&gt;] = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:year&lt;/span&gt;].to_i
&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;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:month&lt;/span&gt;] = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:month&lt;/span&gt;].to_i
&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;After adding this, I tested it in the browser and there were no more checkout errors! I hope you’ve found this interesting and helpful, thanks for reading!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Create a sales functionality within Spree 2.3 using Spree fancy</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/11/create-sales-functionality-within-spree/"/>
      <id>https://www.endpointdev.com/blog/2014/11/create-sales-functionality-within-spree/</id>
      <published>2014-11-14T00:00:00+00:00</published>
      <author>
        <name>Bianca Rodrigues</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;I recently started working with Spree and wanted to learn how to implement some basic features. I focused on one of the most common needs of any e-commerce business—​adding a sale functionality to products. To get a basic understanding of what was involved, I headed straight to the &lt;a href=&#34;https://web.archive.org/web/20170827173154/http://guides.spreecommerce.org/developer/getting_started_tutorial.html&#34;&gt;Spree Developer Guides&lt;/a&gt;. As I was going through the directions, I realized it was intended for the older Spree version 2.1. This led to me running into a few issues as I went through it using Spree’s latest version 2.3.4. I wanted to share with you what I learned, and some tips to avoid the same mistakes I made.&lt;/p&gt;
&lt;h3 id=&#34;set-up&#34;&gt;Set-up&lt;/h3&gt;
&lt;p&gt;I’ll assume you have the prerequisites it lists including Rails, Bundler, ImageMagick and the Spree gem. These are the versions I’m running on my Mac OS X:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ruby&lt;/strong&gt;: 2.1.2p95
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rails&lt;/strong&gt;: 4.1.4&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bundler&lt;/strong&gt;: 1.5.3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ImageMagick&lt;/strong&gt;: 6.8.9-1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spree&lt;/strong&gt;: 2.3.4&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What is Bundler?&lt;/strong&gt; Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed. You can read more about the benefits of using Bundler on their &lt;a href=&#34;http://bundler.io/&#34;&gt;website&lt;/a&gt;. If you’re new to Ruby on Rails and/or Spree, you’ll quickly realize how useful Bundler is when updating your gems.&lt;/p&gt;
&lt;p&gt;After you’ve successfully installed the necessary tools for your project, it’s time to create our first Rails app, which will then be used as a foundation for our simple Spree project called mystore&lt;/p&gt;
&lt;h3 id=&#34;lets-create-our-app&#34;&gt;Let’s create our app&lt;/h3&gt;
&lt;p&gt;Run the following commands:&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:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt; rails &lt;span style=&#34;color:#080&#34;&gt;new&lt;/span&gt; mystore
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt; cd mystore
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt; gem install spree_cmd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;*Note: you may get a warning that you need to run bundle install before trying to start your application since spree_gateway.git isn’t checked out yet. Go ahead and follow those directions, I’ll wait.&lt;/p&gt;
&lt;h3 id=&#34;spree-ify-our-app&#34;&gt;Spree-ify our app&lt;/h3&gt;
&lt;p&gt;We can add the e-commerce platform to our Rails app by running the following command:&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;spree install --auto-accept&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all goes well, you should get a message that says, “Spree has been installed successfully. You’re all ready to go! Enjoy!”. Now the fun part—​let’s go ahead and start our server to see what our demo app actually looks like. Run rails s to start the server and open up a new browser page pointing to the URL localhost:3000.&lt;/p&gt;
&lt;p&gt;*Note—​when you navigate to localhost:3000, watch your terminal—​you’ll see a lot of processes running in the background as the page loads simultaneously in your browser window. It can be pretty overwhelming, but as long as you get a “Completed 200 OK” message in your terminal, you should be good to go! See it below:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/create-sales-functionality-within-spree/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/2014/11/create-sales-functionality-within-spree/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Our demo app actually comes with an admin interface ready to use. Head to your browser window and navigate to http://localhost:3000/admin. The login Spree instructs you to use is &lt;a href=&#34;mailto:spree@example.com&#34;&gt;spree@example.com&lt;/a&gt; and password spree123.&lt;/p&gt;
&lt;p&gt;Once you login to the admin screen, this is what you should see:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/create-sales-functionality-within-spree/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/2014/11/create-sales-functionality-within-spree/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Once you begin to use Spree, you’ll soon find that the most heavily used areas of the admin panel include Orders, Products, Configuration and Promotions. We’ll be going into some of these soon.&lt;/p&gt;
&lt;h3 id=&#34;extensions-in-35-steps&#34;&gt;Extensions in 3.5 steps&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://web.archive.org/web/20170912021339/http://guides.spreecommerce.org/developer/extensions_tutorial.html&#34;&gt;next part&lt;/a&gt; of the Spree documentation suggests adding the spree_fancy extension to our store to update the look and feel of the website, so let’s go ahead and follow the next few steps:&lt;/p&gt;
&lt;h3 id=&#34;step-1-update-the-gemfile&#34;&gt;&lt;strong&gt;Step 1: Update the Gemfile&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We can find our Gemfile by going back to the terminal, and within the mystore directory, type ls to see a list of all the files and subdirectories within the Spree app. You will see the Gemfile there—​open it using your favorite text editor. Add the following line to the last line of your Gemfile, and save it:&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;#39;spree_fancy&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:git&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;git://github.com/spree/spree_fancy.git&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:branch&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;2-1-stable&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the branch it mentions is 2-1-stable. Since you just installed Spree, you are most likely using the latest version, 2-3-stable. I changed my branch in the above gem to &amp;lsquo;2-3-stable&amp;rsquo; to reflect the Spree version I’m currently using. After completing this step, run bundle install to install the gem using Bundler.&lt;/p&gt;
&lt;p&gt;Now we need to copy over the migrations and assets from the spree_fancy extension by running this command in your terminal within your mystore application:&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:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt; bundle &lt;span style=&#34;color:#038&#34;&gt;exec&lt;/span&gt; rails g &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;spree_fancy&lt;/span&gt;:install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-15-weve-hit-an-error&#34;&gt;Step 1.5: We’ve hit an error!&lt;/h3&gt;
&lt;p&gt;At this point, you’ve probably hit a LoadError, and we can no longer see our beautiful Spree demo app, instead getting an error page which says “Sprockets::Rails::Helper::AssetFilteredError in Spree::Home#index” at the top. How do we fix this?&lt;/p&gt;
&lt;p&gt;Within your mystore application directory, navigate to config/intializers/assets.rb file and edit the last line of code by uncommenting it and typing:&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;Rails&lt;/span&gt;.application.config.assets.precompile += &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;%w ( &lt;/span&gt;bx_loader.gif )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now restart your server and you will see your new theme!&lt;/p&gt;
&lt;h3 id=&#34;step-2-create-a-sales-extension&#34;&gt;Step 2: Create a sales extension&lt;/h3&gt;
&lt;p&gt;Now let’s see how to create an extension instead of using an existing one. According to the Spree tutorial, we first need to &lt;em&gt;generate&lt;/em&gt; an extension—​remember to run this command from a directory &lt;em&gt;outside&lt;/em&gt; of your Spree application:&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:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt; spree extension simple_sales&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you do that, cd into your spree_simple_sales directory. Next, run bundle install to update your Spree extension.&lt;/p&gt;
&lt;p&gt;Now you can create a migration that adds a sale_price column to variants using the following command:&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;bundle &lt;span style=&#34;color:#038&#34;&gt;exec&lt;/span&gt; rails g migration add_sale_price_to_spree_variants &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sale_price&lt;/span&gt;:decimal&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once your migration is complete, navigate in your terminal to db/migrate/XXXXXXXXXXXX_add_sale_price_to_spree_variants.rb and add in the changes as shown in the Spree tutorial:&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;AddSalePriceToSpreeVariants&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;    add_column &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:spree_variants&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sale_price&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:decimal&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:precision&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:scale&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&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;Now let’s switch back to our mystore application so that we can add our extension before continuing any development. Within mystore, add the following to your Gemfile:&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;#39;spree_simple_sales&amp;#39;&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;#39;../spree_simple_sales&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will have to adjust the path (&amp;rsquo;../spree_simple_sales&amp;rsquo;) depending on where you created your sales extension.&lt;/p&gt;
&lt;p&gt;Now it’s time to bundle install again, so go ahead and run that. Now we need to copy our migration by running this command in our terminal:&lt;/p&gt;
&lt;p&gt;$ rails g spree_simple_sales:install&lt;/p&gt;
&lt;h3 id=&#34;step-3-adding-a-controller-action-to-homecontroller&#34;&gt;Step 3: Adding a controller Action to HomeController&lt;/h3&gt;
&lt;p&gt;Once the migration has been copied, we need to extend the functionality of Spree::HomeController and add an action that selects “on sale” products. Before doing that, we need to make sure to change our .gemspec file within the spree_simple_sales directory (remember: this is outside of our application directory).&lt;/p&gt;
&lt;p&gt;Open up the spree_simple_sales.gemspec file in your text editor&lt;/p&gt;
&lt;p&gt;Add the following line to the list of dependencies:&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;s.add_dependency &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;‘&lt;/span&gt;spree_frontend&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;’&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run bundle.&lt;/p&gt;
&lt;p&gt;Run $ mkdir -p app/controllers/spree to create the directory structure for our controller decorator. This is where we will create a new file called home_controller_decorator.rb and add the following content to it:&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;Spree&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;HomeController&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;    &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;sale&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:#33b&#34;&gt;@products&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Product&lt;/span&gt;.joins(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variants_including_master&lt;/span&gt;).where(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;spree_variants.sale_price is not null&amp;#39;&lt;/span&gt;).uniq
&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;As Spree explains it, this script will select just the products that have a variant with a sale_price set.&lt;/p&gt;
&lt;p&gt;Next step—​add a route to this sales action in our config/routes.rb file. Make sure your routes.rb file 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:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Core&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Engine&lt;/span&gt;.routes.draw &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;  get &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/sale&amp;#34;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;home#sale&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;h3 id=&#34;lets-set-a-sale-price-for-the-variant&#34;&gt;Let’s set a sale price for the variant&lt;/h3&gt;
&lt;p&gt;Normally, to change a variant attribute, we could do it through the admin interface, but we haven’t created this functionality yet. This means we need to open up our rails console:&lt;/p&gt;
&lt;p&gt;*Note—​you should be in the mystore directory&lt;/p&gt;
&lt;p&gt;Run $ rails console&lt;/p&gt;
&lt;p&gt;The next steps are taken directly from the Spree documentation:&lt;/p&gt;
&lt;p&gt;“Now, follow the steps I take in selecting a product and updating its master variant to have a sale price. Note, you may not be editing the exact same product as I am, but this is not important. We just need one “on sale” product to display on the sales 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;&amp;gt; product = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Product&lt;/span&gt;.first
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#888&#34;&gt;#&amp;lt;Spree::Product id: 107377505, name: &amp;#34;Spree Bag&amp;#34;, description: &amp;#34;Lorem ipsum dolor sit amet, consectetuer adipiscing...&amp;#34;, available_on: &amp;#34;2013-02-13 18:30:16&amp;#34;, deleted_at: nil, permalink: &amp;#34;spree-bag&amp;#34;, meta_description: nil, meta_keywords: nil, tax_category_id: 25484906, shipping_category_id: nil, count_on_hand: 10, created_at: &amp;#34;2013-02-13 18:30:16&amp;#34;, updated_at: &amp;#34;2013-02-13 18:30:16&amp;#34;, on_demand: false&amp;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;&amp;gt; variant = product.master
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#888&#34;&gt;#&amp;lt;Spree::Variant id: 833839126, sku: &amp;#34;SPR-00012&amp;#34;, weight: nil, height: nil, width: nil, depth: nil, deleted_at: nil, is_master: true, product_id: 107377505, count_on_hand: 10, cost_price: #&amp;lt;BigDecimal:7f8dda5eebf0,&amp;#39;0.21E2&amp;#39;,9(36)&amp;gt;, position: nil, lock_version: 0, on_demand: false, cost_currency: nil, sale_price: nil&amp;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;&amp;gt; variant.sale_price = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&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;&amp;gt; variant.save
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hit Ctrl-D to exit the console.&lt;/p&gt;
&lt;p&gt;Now we need to create the page that renders the product that is on sale. Let’s create a view to display these “on sale” products.&lt;/p&gt;
&lt;p&gt;Create the required views directory by running:&lt;/p&gt;
&lt;p&gt;$ mkdir -p app/views/spree/home&lt;/p&gt;
&lt;p&gt;Create the a file in your new directory called sale.html.erb and add the following to it:&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;&amp;lt;div data-hook=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;homepage_products&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= render &amp;#39;spree/shared/products&amp;#39;, :products =&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#33b&#34;&gt;@products&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;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:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now start your rails server again and navigate to localhost:3000/sale to see the product you listed on sale earlier! Exciting stuff, isn’t it? The next step is to actually reflect the sale price instead of the original price by fixing our sales price extension using Spree Decorator.&lt;/p&gt;
&lt;h3 id=&#34;decorate-your-variant&#34;&gt;Decorate your variant&lt;/h3&gt;
&lt;p&gt;Create the required directory for your new decorator within your mystore application:&lt;/p&gt;
&lt;p&gt;$ mkdir -p app/models/spree&lt;/p&gt;
&lt;p&gt;Within your new directory, create a file called variant_decorator.rb and add:&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;Spree&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;Variant&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;    alias_method &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:orig_price_in&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:price_in&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;price_in&lt;/span&gt;(currency)
&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;return&lt;/span&gt; orig_price_in(currency) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; sale_price.present?
&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Price&lt;/span&gt;.new(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.id, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.sale_price, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:currency&lt;/span&gt; =&amp;gt; currency)
&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 original method of price_in now has an alias of price_in unless there is a sale_price present, in which case the sale price is returned on the product’s master variant.&lt;/p&gt;
&lt;p&gt;In order to ensure that our modification to the core Spree functionality works, we need to write a couple of unit tests for variant_decorator.rb. We need a full Rails application present to test it against, so we can create a barebones test_app to run our tests against.&lt;/p&gt;
&lt;p&gt;Run the following command from the root directory of your EXTENSION:&lt;/p&gt;
&lt;p&gt;$ bundle exec rake test_app&lt;/p&gt;
&lt;p&gt;It will begin the process by saying “Generating dummy Rails application…”—​great! you’re on the right path.&lt;/p&gt;
&lt;p&gt;Once you finish creating your dummy Rails app, run the rspec command and you should see the following output:&lt;/p&gt;
&lt;p&gt;No examples found.&lt;/p&gt;
&lt;p&gt;Finished in 0.00005 seconds&lt;/p&gt;
&lt;p&gt;0 examples, 0 failures&lt;/p&gt;
&lt;p&gt;Now it’s time to start adding some tests by replicating your extension’s directory structure in the spec directory:&lt;/p&gt;
&lt;p&gt;$ mkdir -p spec/models/spree&lt;/p&gt;
&lt;p&gt;In your new directory, create a file called variant_decorator_spec.rb and add this test:&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;spec_helper&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;describe &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Variant&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;  describe &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;#price_in&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;    it &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;returns the sale price if it is present&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;      variant = create(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sale_price&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      expected = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Price&lt;/span&gt;.new(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt; =&amp;gt; variant.id, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:currency&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;USD&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt; =&amp;gt; variant.sale_price)
&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;      result = variant.price_in(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;USD&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;      result.variant_id.should == expected.variant_id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      result.amount.to_f.should == expected.amount.to_f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      result.currency.should == expected.currency
&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;    it &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;returns the normal price if it is not on sale&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;      variant = create(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:price&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;15&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      expected = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Price&lt;/span&gt;.new(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt; =&amp;gt; variant.id, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:currency&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;USD&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt; =&amp;gt; variant.price)
&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;      result = variant.price_in(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;USD&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;      result.variant_id.should == expected.variant_id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      result.amount.to_f.should == expected.amount.to_f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      result.currency.should == expected.currency
&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;deface-overrides&#34;&gt;Deface overrides&lt;/h3&gt;
&lt;p&gt;Next we need to add a field to our product admin page, so we don’t have to always go through the rails console to update a product’s sale_price. If we directly override the view that Spree provides, whenever Spree updates the view in a new release, the updated view will be lost, so we’d have to add our customizations back in to stay up to date.&lt;/p&gt;
&lt;p&gt;A better way to override views is to use Deface, which is a Rails library to directly edit the underlying view file. All view customizations will be in ONE location: app/overrides which will make sure your app is always using the latest implementation of the view provided by Spree.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to mystore/app/views/spree and create an admin/products directory and create the file _form.html.erb.&lt;/li&gt;
&lt;li&gt;Copy the full file NOT from Spree’s GitHub but from your Spree backend. You can think of your Spree backend as the area to edit your admin (among other things)—​the spree_backend gem contains the most updated  _form.html.erb—​if you use the one listed in the documentation, you will get some Method Errors on your product page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to find the _form.html.erb file in your spree_backend gem, navigate to your app, and within that, run the command:&lt;/p&gt;
&lt;p&gt;bundle show spree_backend&lt;/p&gt;
&lt;p&gt;The result is the location of your spree_backend. Now cd into that location, and navigate to app/views/spree/admin/products—​this is where you will find the correct _form.html.erb. Copy the contents of this file into the newly created _form.html.erb file within your application’s directory structure you just created: mystore/app/views/spree/admin/products.&lt;/p&gt;
&lt;p&gt;Now we want to actually add a field container after the price field container for sale price so we need to create another override by creating a new file in your application’s app/overrides directory called add_sale_price_to_product_edit.rb and add the following content:&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;Deface&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Override&lt;/span&gt;.new(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:virtual_path&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;spree/admin/products/_form&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:#a60;background-color:#fff0f0&#34;&gt;:name&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;add_sale_price_to_product_edit&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:#a60;background-color:#fff0f0&#34;&gt;:insert_after&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;erb[loud]:contains(&amp;#39;text_field :price&amp;#39;)&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;:text&lt;/span&gt; =&amp;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:#d20;background-color:#fff0f0&#34;&gt;    &amp;lt;%= f.field_container :sale_price do %&amp;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:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;%= f.label :sale_price, raw(Spree.t(:sale_price) + content_tag(:span, &amp;#39; *&amp;#39;)) %&amp;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:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;%= f.text_field :sale_price, :value =&amp;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:#d20;background-color:#fff0f0&#34;&gt;        number_to_currency(@product.sale_price, :unit =&amp;gt; &amp;#39;&amp;#39;) %&amp;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:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;%= f.error_message_on :sale_price %&amp;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:#d20;background-color:#fff0f0&#34;&gt;    &amp;lt;% end %&amp;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:#d20;background-color:#fff0f0&#34;&gt;  &amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last step is to update our model in order to get an updated product edit form. Create a new file in your application’s app/models/spree directory called product_decorator.rb. Add the following content:&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;Spree&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;Product&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;    delegate_belongs_to &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:master&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sale_price&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;Now you can check to see if it worked by heading to http://localhost:3000/admin/products and you should edit one of the products. Once you’re on the product edit page, you should see a new field container called SALE PRICE. Add a sale price in the empty field and click on update. Once completed, navigate to http://localhost:3000/sale to find an updated list of products on sale.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Congratulations, you’ve created the sales functionality! If you’re using Spree 2.3 to create a sales functionality for your application, I would love to know what your experience was like. Good luck!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Authorization Failure for Customized Role</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/09/spree-authorization-failure-for/"/>
      <id>https://www.endpointdev.com/blog/2014/09/spree-authorization-failure-for/</id>
      <published>2014-09-22T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;Hello again all. Recently I was working on another Spree site running Spree 2.1.1. The client wanted to create some custom roles. For example, the client wanted there to be a Sales Manager role. A Sales Manager could log in and have read and write access to all the orders. However, a sales manager should not have read/write access to products, configuration, promotions, users, etc. This was easily accomplished by following the steps in the &lt;a href=&#34;https://guides.spreecommerce.org/developer/security.html&#34;&gt;Spree documentation&lt;/a&gt;. As I will describe, this documentation assumes that the custom role will have access to Orders#index.&lt;/p&gt;
&lt;p&gt;The client wanted to create a second custom role that had create, read, update and delete access to the Training model and nothing more. The training model belongs to a taxon and has a unique event date and taxon id. An example would be a training instance with an event date of September 9th, 2014 that belongs to a taxon with the name “Fire Safety 101” and a description “Teaching fire safety in accordance with OSHA standards. 10 hours and lunch is provided”. So, I planned to create a training personnel role that should be able to log in and &lt;em&gt;only&lt;/em&gt; have read/write access to Trainings. However, the Spree documentation did not provide an explanation on how to create a custom role that does not have read or write access to orders.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/09/spree-authorization-failure-for/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/2014/09/spree-authorization-failure-for/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Following the pattern described in the &lt;a href=&#34;https://guides.spreecommerce.org/developer/security.html&#34;&gt;Spree documentation&lt;/a&gt; for creating custom roles and their respective authorization, I created an ability_decorator.rb with the contents:&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;AbilityDecorator&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;include&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;CanCan&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Ability&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;initialize&lt;/span&gt;(user)
&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; user.respond_to?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:has_spree_role?&lt;/span&gt;) &amp;amp;&amp;amp; user.has_spree_role?(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sales_manager&amp;#39;&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;:admin&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:index&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:show&lt;/span&gt;], &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#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 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;if&lt;/span&gt; user.respond_to?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:has_spree_role?&lt;/span&gt;) &amp;amp;&amp;amp; user.has_spree_role?(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;training&amp;#39;&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;:admin&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:manage&lt;/span&gt;], &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Training&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;   &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Ability&lt;/span&gt;.register_ability(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AbilityDecorator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, after creating a training user and attempting to log in, I got an unauthorized error. So, I checked the logs:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/09/spree-authorization-failure-for/image-1.png&#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/2014/09/spree-authorization-failure-for/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The log output above shows that while I was logged in as a user with the training role, the application was checking for authorization on Spree::Admin::OrdersController#index (the orders list page), because the base admin URL (&amp;quot;/admin&amp;quot;) points to this controller action. I reviewed the &lt;a href=&#34;http://rdoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers:after_sign_in_path_for&#34;&gt;Devise documentation&lt;/a&gt; to modify where a user with the training role is redirected to upon login (via &lt;a href=&#34;https://github.com/spree/spree_auth_devise&#34;&gt;Spree Auth Devise’s after_sign_in method&lt;/a&gt;), as shown in the code shown below.&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;after_sign_in_path_for&lt;/span&gt;(resource)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stored_location_for(resource) ||
&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; resource.is_a?(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;User&lt;/span&gt;) &amp;amp;&amp;amp; resource.has_spree_role?(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;training&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        admin_trainings_path
&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;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;super&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;After making this change, I tried once again and was able to successfully log in as a training user and &lt;em&gt;only&lt;/em&gt; have the desired access to Trainings.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/09/spree-authorization-failure-for/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/2014/09/spree-authorization-failure-for/image-2.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;To summarize, if you’d like to have a custom role and &lt;em&gt;not&lt;/em&gt; give them access to Orders, you will need to make some adjustments outside the steps listed in Spree’s documentation for custom role authorization.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Enhancing the labelsontime.com Spree application</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/09/enhancing-labelsontimecom-spree/"/>
      <id>https://www.endpointdev.com/blog/2014/09/enhancing-labelsontimecom-spree/</id>
      <published>2014-09-09T00:00:00+00:00</published>
      <author>
        <name>Bianca Rodrigues</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;http://www.labelsontime.com&#34;&gt;Labels on Time&lt;/a&gt; is an online retailer that delivers top-quality thermal roll and direct thermal labels—​and all on time, of course. They came to us last year to upgrade their Spree site, resolve bugs, and develop cutting-edge features, utilizing our expertise with the ecommerce platform. Spree Commerce is an open-source ecommerce solution built on Ruby on Rails, and manages all aspects of the fulfillment process, from checkout to shipping to discounts, and much more.&lt;/p&gt;
&lt;h3 id=&#34;upgrading-the-spree-platform&#34;&gt;UPGRADING THE SPREE PLATFORM&lt;/h3&gt;
&lt;p&gt;There were quite a few challenges associated with the upgrade, since Labels on Time was still running on Spree’s version 2.0, which was not yet stable. To keep some stability, we initially worked off a fork of Spree, and selectively brought in changes from 2.0 when we were sure they were stable and reliable enough.&lt;/p&gt;
&lt;h3 id=&#34;using-spree-gems&#34;&gt;USING SPREE GEMS&lt;/h3&gt;
&lt;p&gt;To date, some of the Spree gems we have used on the site include:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/spree-contrib/spree_active_shipping&#34;&gt;Active Shipping&lt;/a&gt;: This is a Spree plugin that can interface with USPS, UPS and FedEx. Label on Time’s active_shipping gem interacts with the UPS API, which is a big task to tackle since it requires a lot of configuration, especially every time Spree is updated.&lt;/p&gt;
&lt;p&gt;Another important gem we use for Labels on Time is &lt;a href=&#34;https://github.com/spree/spree_volume_pricing&#34;&gt;Volume Pricing&lt;/a&gt;. Volume Pricing is an extension to Spree that uses predefined ranges of quantities to determine the price for a particular product variant. When we first added this gem on the labelsontime.com checkout page, we kept finding that if a user increased the number of items in their cart sufficiently to activate the volume pricing and receive a discount per item, the standard Spree view did not show the new (discounted) price that was currently in effect (although it was correctly calculating the totals). To resolve this, our developer &lt;a href=&#34;/blog/authors/matt-galvin/&#34;&gt;Matt Galvin&lt;/a&gt; created some custom JavaScript and Ruby code. Thanks to Matt’s ingenuity, the application can now return every price for every possible size and sort it accordingly.&lt;/p&gt;
&lt;h3 id=&#34;what-were-working-on-next&#34;&gt;WHAT WE’RE WORKING ON NEXT&lt;/h3&gt;
&lt;p&gt;Matt recently upgraded the application to 2.0.10, which was needed for security reasons. You can read more about the security fix &lt;a href=&#34;https://spreecommerce.org/pages/blog/security-update-spree-2&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are also working on implementing a neat SEO gem called &lt;a href=&#34;https://github.com/jumph4x/canonical-rails&#34;&gt;Canonical Rails&lt;/a&gt;, which helps search engines understand that any duplicate content URLs it can access all refer to the canonical URL.&lt;/p&gt;
&lt;p&gt;Next up, we’re going to implement inventory management, where, according to a customer’s location, we can suggest the available inventory in the closest warehouse to that location.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Commerce, Take Care When Offering Free Shipping Promotion</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/08/spree-commerce-take-care-when-offering/"/>
      <id>https://www.endpointdev.com/blog/2014/08/spree-commerce-take-care-when-offering/</id>
      <published>2014-08-20T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;Hello again all. I was working on another &lt;a href=&#34;https://guides.spreecommerce.org/&#34;&gt;Spree Commerce Site&lt;/a&gt;, a Ruby on Rails based e-commerce platform. As many of you know, Spree Commerce comes with &lt;a href=&#34;https://guides.spreecommerce.org/developer/promotions.html&#34;&gt;Promotions&lt;/a&gt;. According to Spree Commerce documentation, Spree Commerce Promotions are:&lt;/p&gt;
&lt;p&gt;“&amp;hellip; used to provide discounts to orders, as well as to add potential additional items at no extra cost. Promotions are one of the most complex areas within Spree, as there are a large number of moving parts to consider.”&lt;/p&gt;
&lt;p&gt;The promotions feature can be used to offer discounts like free shipping, buy one get one free etc.. The client on this particular project had asked for the ability to provide a coupon for free shipping. Presumably this would be a quick and easy addition since these types of promotions are included in Spree.&lt;/p&gt;
&lt;p&gt;The site in question makes use of &lt;a href=&#34;https://github.com/spree/spree_active_shipping&#34;&gt;Spree’s Active Shipping Gem&lt;/a&gt;, and plugs in the &lt;a href=&#34;https://web.archive.org/web/20170709150700/https://www.ups.com/us/en/services/technology-integration/online-tools-shipping.page&#34;&gt;UPS Shipping API&lt;/a&gt; to return accurate and timely shipping prices with the UPS carrier.&lt;/p&gt;
&lt;p&gt;The client offers a variety of shipping methods including Flat Rate Ground, Second Day Air, 3 Day Select, and Next Day Air. Often, Next Day Air shipping costs several times more than Ground. E.g.: If something costs $20 to ship Ground, it could easily cost around $130 to ship Next Day Air.&lt;/p&gt;
&lt;p&gt;When creating a free shipping Promotion in Spree it’s important to understand that by default it will be applied to all shipping methods. In this case, the customer could place a small order, apply the coupon and receive free Next Day Air shipping! To take care of this you need to use &lt;a href=&#34;https://guides.spreecommerce.org/developer/promotions.html#rules&#34;&gt;Promotion Rules&lt;/a&gt;. Spree comes with several built-in rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First Order: The user’s order is their first.&lt;/li&gt;
&lt;li&gt;ItemTotal: The order’s total is greater than (or equal to) a given value.&lt;/li&gt;
&lt;li&gt;Product: An order contains a specific product.&lt;/li&gt;
&lt;li&gt;User: The order is by a specific user.&lt;/li&gt;
&lt;li&gt;UserLoggedIn: The user is logged in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see there is no built in Promotion Rule to limit the free shipping to certain shipping methods. But fear not, it’s possible to &lt;a href=&#34;https://guides.spreecommerce.org/developer/promotions.html#registering-a-new-rule&#34;&gt;create a custom rule&lt;/a&gt;.&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;Spree&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Promotion&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;Rules&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;RestrictFreeShipping&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PromotionRule&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;MATCH_POLICIES&lt;/span&gt; = &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;%w(all)&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;eligible?&lt;/span&gt;(order, options={})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             e = &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 style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; order.shipment.shipping_method.admin_name == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UPS Flat Rate Ground&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               e = &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:#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;               e = &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 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;return&lt;/span&gt; e
&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;Note that you have to create a partial for the rule, as per the &lt;a href=&#34;https://dev-docs.spreecommerce.org/internals/promotions#rules&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then, in config/locales/en.yml I added a name and description for the rule.&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:#a60;background-color:#fff0f0&#34;&gt;en&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;spree&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;promotion_rule_types&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;restrict_free_shipping&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;name&lt;/span&gt;: &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Restrict&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Free&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Shipping&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;To&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Ground&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;description&lt;/span&gt;: &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;If&lt;/span&gt; somebody uses a free shipping coupon it should only apply to ground shipping&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last step was to restart the app and configure the promotion in the Spree Admin interface.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/08/spree-commerce-take-care-when-offering/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/2014/08/spree-commerce-take-care-when-offering/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/08/spree-commerce-take-care-when-offering/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/2014/08/spree-commerce-take-care-when-offering/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;

      </content>
    </entry>
  
    <entry>
      <title>Unable to Bcc in mail, Spree 2.0 Stable Rails 3.2.14</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/06/unable-to-bcc-in-mail-spree-20-stable/"/>
      <id>https://www.endpointdev.com/blog/2014/06/unable-to-bcc-in-mail-spree-20-stable/</id>
      <published>2014-06-09T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;Hello again all.  As usual, I was working on a &lt;a href=&#34;https://spreecommerce.org/&#34;&gt;Spree Commerce&lt;/a&gt; website.  I recently encountered an issue when trying to bcc order confirmation emails.  Others have been asking about this on &lt;a href=&#34;https://github.com/spree/spree/issues/4484&#34;&gt;Github&lt;/a&gt; and also on the &lt;a href=&#34;https://groups.google.com/forum/#!msg/spree-user/50yjID6znOE/QSr51V1xgrUJ&#34;&gt;Spree mailing list&lt;/a&gt;, so it was time to write about the problem and the solution that worked for me.&lt;/p&gt;
&lt;p&gt;First, I’d like to briefly describe the use case here.  As with any typical e-commerce site, a user visits the site, adds some items to their cart, and checks out.  After which, an order confirmation (order summary) email is sent to the user with their order details and any extra information provided by the seller.&lt;/p&gt;
&lt;p&gt;Spree pretty much handles all this for you automatically.  What about if you as the business owner would like a copy of this e-mail?  Easy enough.  If you review the &lt;a href=&#34;https://web.archive.org/web/20160606002703/http://guides.spreecommerce.org/user/configuring_mail_methods.html&#34;&gt;Spree documentation&lt;/a&gt; you will see simple instructions for the  “Mail Method Settings” to set up in the Spree Admin Interface.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/06/unable-to-bcc-in-mail-spree-20-stable/image-0.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/2014/06/unable-to-bcc-in-mail-spree-20-stable/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Ok, so let’s say you follow all the instructions and start placing test orders (or receiving real ones), and you’re not getting bcc’d?  This is where it gets tricky, so let’s check out a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Check the logs&lt;/p&gt;
&lt;p&gt;Check to see if Spree/Rails is attempting to send the confirmation email to your Bcc recipient&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try development, test, &amp;amp; production modes&lt;/p&gt;
&lt;p&gt;If the Bcc email is not getting sent, keep following along for the solution&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure the interceptor works by adding a value into “INTERCEPT EMAIL ADDRESS” via the admin.  If emails are being intercepted you know the the interceptor is working.  Why is that important? Because, that is also where the bcc code is.&lt;/p&gt;
&lt;p&gt;core/lib/spree/mail_interceptor.rb&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;module Spree
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  module Core
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    class MailInterceptor
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      def self.delivering_email(message)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return unless MailSettings.override?
&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;        if Config[:intercept_email].present?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          message.subject = &amp;#34;#{message.to} #{message.subject}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          message.to = Config[:intercept_email]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end
&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;        if Config[:mail_bcc].present?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          message.bcc ||= Config[:mail_bcc]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can ensure that more than one email can be sent by updating &lt;code&gt;mail(to: @order.email, from: from_address, subject: subject)&lt;/code&gt; to be an array of emails, like &lt;code&gt;mail(to: [@order.email, &amp;quot;some_other_email@somewhere.com&amp;quot;], from: from_address, subject: subject)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;core/app/mailers/spree/order_mailer.rb&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;Spree&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OrderMailer&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;BaseMailer&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;confirm_email&lt;/span&gt;(order, resend = &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 style=&#34;color:#33b&#34;&gt;@order&lt;/span&gt; = order.respond_to?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;) ? order : &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.find(order)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      subject = (resend ? &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;Spree&lt;/span&gt;.t(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:resend&lt;/span&gt;).upcase&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;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      subject += &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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:site_name&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; &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;Spree&lt;/span&gt;.t(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;order_mailer.confirm_email.subject&amp;#39;&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; #&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#33b&#34;&gt;@order&lt;/span&gt;.number&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;      mail(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;to&lt;/span&gt;: &lt;span style=&#34;color:#33b&#34;&gt;@order&lt;/span&gt;.email, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;from&lt;/span&gt;: from_address, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;subject&lt;/span&gt;: subject)
&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;cancel_email&lt;/span&gt;(order, resend = &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 style=&#34;color:#33b&#34;&gt;@order&lt;/span&gt; = order.respond_to?(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;) ? order : &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.find(order)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      subject = (resend ? &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;Spree&lt;/span&gt;.t(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:resend&lt;/span&gt;).upcase&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;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      subject += &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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:site_name&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; &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;Spree&lt;/span&gt;.t(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;order_mailer.cancel_email.subject&amp;#39;&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; #&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#33b&#34;&gt;@order&lt;/span&gt;.number&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;      mail(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;to&lt;/span&gt;: &lt;span style=&#34;color:#33b&#34;&gt;@order&lt;/span&gt;.email, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;from&lt;/span&gt;: from_address, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;subject&lt;/span&gt;: subject)
&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;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, you’ve verified the interceptor works, and the mailer can definitely send more than one email.  Here is the final piece that is not mentioned anywhere on the Spree site, or any resolutions for any posts I had found: you may need to contact your hosting provider and ask them to make any necessary adjustments to allow for the bcc messages.  I saw that several people have gotten hung up on this and I hope this post has saved you some time.  If you are still having trouble, please feel free to reach out in the comments.  You can refer to the &lt;a href=&#34;https://github.com/spree/spree/issues/4484&#34;&gt;Spree issue&lt;/a&gt; I created on this topic some time ago.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Security Update 2.x.x Error, undefined method ‘assume_from_symbol’ for Money:Class (ActionView::Template::Error)</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/04/spree-security-update-2xx-error/"/>
      <id>https://www.endpointdev.com/blog/2014/04/spree-security-update-2xx-error/</id>
      <published>2014-04-23T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;On March 25, 2014 Spree Commerce posted an announcement that there was a security vulnerability for all Spree 2.x.x versions. As reported on &lt;a href=&#34;https://spreecommerce.org/pages/blog/security-update-spree-2&#34;&gt;Spree’s website&lt;/a&gt;,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;The exploit would require the attacker to randomly guess valid order numbers, but once achieved, the technique would reveal private customer information associated with the order. &lt;strong&gt;Credit card details are never stored in Spree and were never at risk by this exploit.&lt;/strong&gt;&amp;rdquo;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So, this update is pretty typical and as usual, you should make sure nothing has broken with any changes that were made. I wanted to note a couple of &amp;ldquo;gotchas&amp;rdquo; with this most recent update however.&lt;/p&gt;
&lt;p&gt;The first of which is probably obvious and really more of a reminder as sometimes people forget. Further, the &lt;a href=&#34;https://spreecommerce.org/pages/blog/security-update-spree-2&#34;&gt;instructions&lt;/a&gt; for this update in the Spree docs don’t mention it, so, as a reminder - after updating be sure to run&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-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bundle &lt;span style=&#34;color:#038&#34;&gt;exec&lt;/span&gt; rake railties:install:migrations
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bundle &lt;span style=&#34;color:#038&#34;&gt;exec&lt;/span&gt; rake db:migrate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, here is the tricky one. After updating and installing/running your migrations you may find that you are getting 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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;undefined method `assume_from_symbol&amp;#39; for Money:Class (ActionView::Template::Error)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I have been unable to find anything on this in the Spree documentation but the problem is that in the update the money gem version 6.1.1 breaks the product display. The work around is to lock the money gem at 6.0.1. So, specify version 6.0.1 for the money gem in your Gemfile&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;#39;money&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;=6.0.1&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Updating Spree versions can sometimes be a little tense if things start breaking and you don’t know why. I hope that if this error brought you here this article has saved you some time.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Active Shipping Gem “We are unable to calculate shipping rates for the selected items.” Error</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/02/spree-active-shipping-gem-we-are-unable/"/>
      <id>https://www.endpointdev.com/blog/2014/02/spree-active-shipping-gem-we-are-unable/</id>
      <published>2014-02-12T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;I was recently working on a &lt;a href=&#34;http://spreecommerce.org/&#34;&gt;Spree&lt;/a&gt; site and setting up the &lt;a href=&#34;https://github.com/spree/spree_active_shipping&#34;&gt;Spree Active Shipping Gem&lt;/a&gt; along with the UPS API. For those not familiar, the Spree Active Shipping Gem interacts with various shipping APIs like UPS, USPS, and FedEx. Due to the nature of Spree—​where it does so much for you, and the interaction between the Active Shipping Gem and a shipping API also being “auto-magic”, it is often difficult to debug. As I was recently undertaking the task of setting this up I found a few “gotchas” that I hope, through this blog post, may be able to save others a lot of time.&lt;/p&gt;
&lt;p&gt;I have found that there wasn’t a lot of instruction for setting up the Active Shipping Gem and a shipping carrier API like the UPS Shipping API. Ostensibly, there isn’t much to it—​the Active Shipping Gem handles much of the interaction between the shipping API of choice and Spree.&lt;/p&gt;
&lt;p&gt;First, you’re going to go the &lt;a href=&#34;https://github.com/spree/spree_active_shipping&#34;&gt;Spree Active Shipping Gem&lt;/a&gt; GitHub repo and follow the instructions for installing the Active Shipping Gem. It is very straightforward, but do proceed in the order mentioned in the Spree Active Shipping Gem documentation as some steps depend on the successful completion of others.&lt;/p&gt;
&lt;p&gt;Second, you’re going to go to the shipper of your choice, in this case UPS, and follow their directions for using their &lt;a href=&#34;https://web.archive.org/web/20160101071458/http://www.ups.com/content/us/en/bussol/browse/cat/developer_kit.html&#34;&gt;API&lt;/a&gt;. I do recommend actually reading, or at least skimming, the pages and pages of documentation. Why? Because there are some important agreements explaining how the API is to be used (basically legal requirements for the UPS logo).&lt;/p&gt;
&lt;p&gt;The Active Shipping Gem makes a call to the API, the API returns a hash of various shipping methods and prices based on the parameters you’ve sent it (such as shipment origin and destination), and then it automatically displays in the UI as an adjustment. How great is that?!&lt;/p&gt;
&lt;p&gt;Well, it would be great if it all worked out exactly as planned. However, if you are running Spree 2-0-stable you may find yourself battling an unusual circumstance. Namely, Spree 2-0-stable will create your core/app/views/spree/checkout/edit.html.erb as&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;&amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% content_for &lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:head&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;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:#d20;background-color:#fff0f0&#34;&gt;     &amp;lt;%= javascript_include_tag &amp;#39;/states&amp;#39; %&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% end &lt;/span&gt; %&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will provide the incorrect path. It is intended to hit the StatesController, so update it like so:&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;&amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% content_for &lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:head&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;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:#d20;background-color:#fff0f0&#34;&gt;     &amp;lt;%= javascript_include_tag states_url %&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% end &lt;/span&gt; %&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, once this correction has been made you may find that you are still having an error, “We are unable to calculate shipping rates for the selected items.”&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2014/02/spree-active-shipping-gem-we-are-unable/image-0.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/02/spree-active-shipping-gem-we-are-unable/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At this point Chrome Dev Tools will not show any errors. When I had this error, a number of Google searches returned results of the kind &amp;ldquo;make sure you have set up your shipping zones correctly and added shipping methods to these zones&amp;rdquo;. I verified this again and again in the console, as did many others who were equally perplexed by this message on &lt;a href=&#34;http://stackoverflow.com/questions/18277367/spree-commerce-error-on-checkout-we-are-unable-to-ship-the-selected-items-to-y&#34;&gt;StackOverflow&lt;/a&gt; as well as in Google Groups, like &lt;a href=&#34;https://groups.google.com/forum/#!msg/spree-user/aCJz5iNemfo/3v4uJ8hPBVsJ&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;https://groups.google.com/forum/#!topic/spree-user/aCJz5iNemfo&#34;&gt;here&lt;/a&gt;. Some got this error when they added an item to the cart that had a count_on_hand of 0 and backorderable equal to false, like you can see here at &lt;a href=&#34;https://github.com/spree/spree/issues/3521&#34;&gt;Spree GitHub issues&lt;/a&gt;. If a 0 count_on_hand is what is giving you this error, but you want a product to be backorderable, make sure to also check the “Propagate All Variants” in the Spree admin as seen below. This will loop through all of the product’s variants with a count_on_hand of 0, and allow them to be backorderable.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2014/02/spree-active-shipping-gem-we-are-unable/image-1-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/02/spree-active-shipping-gem-we-are-unable/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After a long while of searching and wondering, is it the API? Is it the Active Shipping Gem? Is it a blacklisted zip code? I went through and changed one setting at a time in the Spree admin until finally arriving at the source of this error for me. Missing product weight. Because UPS needs the product weight in order to calculate shipping charges, make sure this is set.&lt;/p&gt;
&lt;p&gt;The “We are unable to calculate shipping rates for the selected items” error message is misleading. If you encounter this error after correcting the javascript_include_tag, the cause is most likely a setting in the admin. Check for how insufficient inventory is handled, missing product weights, or incorrectly setup up or non-existent shipping zones &amp;amp; associated methods. I hope if this error message is what brought you here that this post has saved you some time.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>A Brief Retrospective of Spree</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/02/a-brief-retrospective-of-spree/"/>
      <id>https://www.endpointdev.com/blog/2014/02/a-brief-retrospective-of-spree/</id>
      <published>2014-02-10T00:00:00+00:00</published>
      <author>
        <name>Tim Case</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;https://web.archive.org/web/20140227013404/http://spreeconf.com:80/&#34;&gt;SpreeConf NYC 2014&lt;/a&gt; starts on the 26th and its hard to believe that Spree is almost 7 years old! Here’s a retrospective showing some notable Spree moments and major releases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;July 15, 2007&lt;/strong&gt; RailsCart, the precursor to Spree, is created by Sean Schofield and gets its first commit as a Google Code project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;February 1, 2008&lt;/strong&gt; Sean is hired by End Point Corporation to work on RailsCart along with its other Rails developers. End Point sponsors Spree for the next year and a half.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;February 15, 2008&lt;/strong&gt; RailsCart project moves from Google Code to GitHub and is renamed Spree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;June 1, 2008&lt;/strong&gt; Spree 0.0.9 released: sophisticated data model for inventory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;June 3, 2008&lt;/strong&gt; Product Properties added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;June 5, 2008&lt;/strong&gt; Spree 0.2.0 released: adds Spree extensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;July 4, 2008&lt;/strong&gt; Zones introduced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sept 8, 2008&lt;/strong&gt; Refactored to REST routing added state machine to order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;October 1, 2008&lt;/strong&gt; New Taxonomy system introduced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;October 2, 2008&lt;/strong&gt; Spree 0.4.0 released: Taxonomy, and VAT-inclusive pricing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;November 26, 2008&lt;/strong&gt; Volume pricing introduced as an extension.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;November 24, 2008&lt;/strong&gt; Spree 0.5.0 released: new shipping framework.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;December 3, 2008&lt;/strong&gt; SEO friendly URLs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;December 4, 2008&lt;/strong&gt; Switched from attachment_fu to paperclip for image attachments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;February 3, 2009&lt;/strong&gt; Spree 0.6.0 released: friendly urls, attachment_fu to paperclip, paranoid deletion of products, standardized security model, rake tasks for translations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;March 5, 2009&lt;/strong&gt; Searchlogic gem added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;March 10, 2009&lt;/strong&gt; Spree 0.7.0 released: switches to ajax-based Magento-style single page checkout, order number as permalinks, French, Russian and Norwegian translations added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;April 2, 2009&lt;/strong&gt; Authlogic gem added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;April 23, 2009&lt;/strong&gt; Spreecommerce.com launched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 4, 2009&lt;/strong&gt; Spree 0.8.0 released: CSS based layout using compass, sass, and blueprint, Authlogic, guest checkout, expanded functionality for prototype products, improved upgrade rake task, improved order security using tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 18, 2009&lt;/strong&gt; Spree core team formalized.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 28, 2009&lt;/strong&gt; Sean Schofield leaves End Point and founds Rails Dog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;September 22, 2009&lt;/strong&gt; Spree 0.9.0 released: Coupon and discount support, improved system of calculators, Thai, Hebrew, Dutch, Finish, and Mexican Spanish translations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;September 5, 2009&lt;/strong&gt; Spree now 100% jQuery for all JavaScript functionality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;March 13, 2009&lt;/strong&gt; Spree 0.10.0 released: Named scopes and product groups, pluggable search (Xapian, Sphinx, and Solr), theming, switch to multipage checkout, improved gateway configuration, multiple payment methods, refunds and credits, restful api.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;June 14, 2010&lt;/strong&gt; Spree 0.11.0 released: new look for Spree, Rails 2.3.8 compatibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;November 1, 2010&lt;/strong&gt; New extension registry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;November 9, 2010&lt;/strong&gt; Spree 0.30.0 released: Rails 3 support, reorganization into multiple gems: spree_core, spree_auth, spree_api, spree_dash, spree_sample, Rails engines introduced, site extension removed, extensions become gems, improved payments with addition of state machine, simplification of adjustments, new promotion functionality, no more “vendor mode”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;December 10, 2010&lt;/strong&gt; Spree 0.40.0 released: switched from Authlogic to Devise for authentication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;March 23, 2011&lt;/strong&gt; Spree 0.50.0 released: improved test coverage, replaced Searchlogic gem with Meta search gem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 13, 2011&lt;/strong&gt; Spree 0.60.0 released: removal of Resource Controller.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;October 10, 2011&lt;/strong&gt; Announcement that Spree Commerce Inc. is formed and raised $1.5M in seed funding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;November 7, 2011&lt;/strong&gt; Spree 0.70.0 Integration with rails asset pipeline, theming support, themes as engines, extension generator, promotions extended.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;January 31, 2012&lt;/strong&gt; Spree analytics with Jiraffe introduced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;February 9, 2012&lt;/strong&gt; Spree 1.0 released, namespacing introduced, Spree referenced routes, mounting Spree engine in routes, Spree analytics, Spree dash gem, command line tool, default payment gateway Spree/Skrill Spree_usa_epay, moved gateways out of core to Spree gateway gem, Refactored preferences, polymorphic adjustments, removed helpers and JavaScript related to VAT, removed sales tax and VAT calculators.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;February 18, 2012&lt;/strong&gt; First SpreeConf held in New York City.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;April 30, 2012&lt;/strong&gt; Spree 1.1.0 released: support for Rails 3.2, Ransack replaces meta search, Spree product groups is a standalone extension, theme support deprecated in favor of deface, major rewrite of credit card model, API rewrite, stronger mass assignment protection, clearer separation between Spree components, easier Spree spec testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;August 31, 2012&lt;/strong&gt; Spree 1.2.0 released: Auth component completely removed from Spree and placed in separate Spree auth devise extension. Customizing state machine no longer requires completely overriding it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;December 19, 2012&lt;/strong&gt; Spree 1.3.0 released: Admin section redesigned, currency support.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;December 20, 2012&lt;/strong&gt; New Spree “Fancy” theme introduced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;April 15, 2013&lt;/strong&gt; New documentation site launched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 19, 2013&lt;/strong&gt; Spree 2.0.0 released: Removed support for Ruby 1.8.7, Backend and Frontend split from core, Split shipments introduced, I18n names spaced translations, New API endpoints, instance level permission in API, Custom API templates, adjustment state changes, Order Populator in its own class, coupon applicator in its own class, product duplicator moved to its own class, new helpers to modify checkout flow steps, API supports “checking out” order, Auto-rotation of images, Unique payment identifier added to payments, removal of state call back in checkout controller, tracking URL for shipments, SSLRequirement deprecated in favor of ForceSSL, MailMethod model no longer exists,&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 21, 2013&lt;/strong&gt; Second SpreeConf held in Washington DC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;September 16, 2013&lt;/strong&gt; Spree 2.1.0 released: Rails 4 compatibility, breaking API changes, better Spree PayPal Express extension.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to Dynamically Update A Spree Product’s Price Based on Volume Pricing</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/11/how-to-dynamically-update-spree/"/>
      <id>https://www.endpointdev.com/blog/2013/11/how-to-dynamically-update-spree/</id>
      <published>2013-11-08T00:00:00+00:00</published>
      <author>
        <name>Matt Galvin</name>
      </author>
      <content type="html">
        &lt;p&gt;I was recently working on a Spree Commerce site that utilizes Spree’s Volume Pricing extension. For those who may not be familiar, the &lt;a href=&#34;https://github.com/spree/spree_volume_pricing&#34;&gt;Spree Commerce Volume Pricing extension&lt;/a&gt; allows a user to offer a variety of ‘price ranges’. These price ranges represent discounted prices per unit for larger quantity orders. For example (we will use this t-shirt pricing table for the remainder of the post) from the &lt;strong&gt;&lt;a href=&#34;https://github.com/spree/spree_volume_pricing&#34;&gt;Spree Volume Pricing Github&lt;/a&gt;&lt;/strong&gt;&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;   Variant                Name               Range        Amount         Position
&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;   Rails T-Shirt          1-5                (1..5)       19.99          1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Rails T-Shirt          6-9                (6...10)     18.99          2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Rails T-Shirt          10 or more         (10+)        17.99          3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I would like to mention that these ranges, although resembling traditional ranges, are expressed as Strings as this will become more important later. Again from the &lt;strong&gt;&lt;a href=&#34;https://github.com/spree/spree_volume_pricing&#34;&gt;Spree Volume Pricing&lt;/a&gt;&lt;/strong&gt; project page at Github,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“All ranges need to be expressed as Strings and must include parentheses. ‘(1..10)’ is considered to be a valid range. ‘1..10’ is not considered to be a valid range (missing the parentheses.)&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Now that the intent of Volume Pricing has been discussed I would like to bring your attention to what is likely a very common use case. Often on an e-commerce website when placing an order for an item the price and quantity is seen as so,&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/how-to-dynamically-update-spree/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/2013/11/how-to-dynamically-update-spree/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Which is generated from relatively typical Spree models and functions in erb and the HTML5 number input:&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;#price per shirt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= display_price(@product) %&amp;gt; per shirt
&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:#d20;background-color:#fff0f0&#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:#d20;background-color:#fff0f0&#34;&gt;  #number field
&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:#d20;background-color:#fff0f0&#34;&gt;  &amp;lt;%=&lt;/span&gt; number_field_tag (&lt;span style=&#34;color:#33b&#34;&gt;@product&lt;/span&gt;.variants_and_option_values.any? ? &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:quantity&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;variants[&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#33b&#34;&gt;@product&lt;/span&gt;.master.id&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:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:class&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:min&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;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:#d20;background-color:#fff0f0&#34;&gt;  &amp;lt;%= button_tag :class =&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;large primary&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;add-to-cart-button&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:type&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:submit&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;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:#d20;background-color:#fff0f0&#34;&gt;    &amp;lt;%= Spree.t(:add_to_cart) %&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% end &lt;/span&gt; %&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, without any additional coding when a customer increases their order quantity to the next range, the price per unit (shirt) should be decremented as noted in the table above. However, as we can see here rather than the price being lowered to 18.99 per shirt, it continues to indicate 19.99 even though volume pricing has taken effect and the shirts are actually 18.99 each.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/how-to-dynamically-update-spree/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/2013/11/how-to-dynamically-update-spree/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;So, how would one accomplish this? JavaScript is the first thing that comes to most people’s mind. I spent some time looking around the Spree docs thinking that certainly there must be something quick to drop in, but there was not. I did a little Googling and found the same thing to be true—​not much info out there on how best to proceed with this task. I was very surprised to find no notes on anyone doing what I would think is a very common issue. So, here we are and I hope that anyone who reads this finds it helpful.&lt;/p&gt;
&lt;h3 id=&#34;step-1a-create-an-array-of-the-prices&#34;&gt;&lt;strong&gt;Step 1a: Create an array of the prices&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;This is the most challenging part of the task. After discussing the issue with some colleagues I believed the easiest method was to create an array of all the possible volume prices. Then, this price could be referenced by just taking the selected quantity of an order, subtracting 1 (to account for the zero-indexing of arrays) and getting the value of the complete volume price array via that index. In this example, using the data from the table above, the array would look like this:&lt;/p&gt;
&lt;p&gt;[19.99, 19.99, 19.99, 19.99, 19.99,18.99,18.99,18.99,18.99]&lt;/p&gt;
&lt;p&gt;In case that isn’t clear from above, volume price (1..5) is 19.99 so the first 5 items in the array are 19.99. Volume price 18.99 is in effect for range (6..9) so the 6th through 9th item in the array are 18.99. If a user were to indicate a quantity of 5, 5-1 = 4. Index 4 of the array is 19.99, the correct price for five shirts. &lt;strong&gt;Note, for now I’ve left off the (10+) range and associated pricing and the reason will be clear in a few moments.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Alright, so now on how to create this array. Those of you who are familiar with Spree know that we use the Spree Model decorator, in this case, the Product decorator which should be created in app/models/product_decorator.rb&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Product&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;
&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;all_prices&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price_ranges = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Variant&lt;/span&gt;.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;product_id&lt;/span&gt;: &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.id).first.volume_prices[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;...-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:range&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    volume_prices = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Variant&lt;/span&gt;.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;product_id&lt;/span&gt;: &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.id).first.volume_prices[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;...-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt;).map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:to_f&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price_ranges.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:to_range&lt;/span&gt;).map{|v| v.map{|i| volume_prices[price_ranges.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:to_range&lt;/span&gt;).index(v)]}}.flatten
&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:#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;step-1b-create-to_&#34;&gt;&lt;strong&gt;Step 1b: Create to_range function for Strings &amp;amp; create a function to return lowest possible price per unit&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now here you may note the to_range call in pink above. As mentioned in this post and in the Volume Pricing docs, Spree expresses these ranges as Strings and not true ranges, so I used this to_range method in lib/range.rb to easily convert the String ranges into true Ranges, which I found on the &amp;ldquo;Master Ruby/Rails Programming&amp;rdquo; post at &lt;a href=&#34;http://athikunte.blogspot.com/2008/02/convert-string-to-range.html&#34;&gt;athikunte blog&lt;/a&gt;. I would also like to draw your attention to the fact that I am taking all but the last item of the volume prices array ([0&amp;hellip;-1]). Why? Because ‘10+’ will not be converted into a range and any quantity of 10 or greater can just get the lowest volume price. Perhaps most importantly, if some product’s last range is 10+ while another is say 25+, this method of obtaining the lowest discounted price will avoid any problems related to that variance. In lib/string.rb,&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;to_range&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;case&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.count(&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;when&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        elements = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.split(&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;return&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Range&lt;/span&gt;.new(elements[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].to_i, elements[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].to_i)
&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;when&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        elements = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.split(&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;return&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Range&lt;/span&gt;.new(elements[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].to_i, elements[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].to_i-&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;else&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;.new(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Couldn&amp;#39;t convert to Range: &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&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;app/models/product_decorator.rb&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;lowest_discounted_volume_price&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Variant&lt;/span&gt;.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;product_id&lt;/span&gt;: &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.id).first.volume_prices[-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].to_f
&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;step-2-load-your-new-volume-pricing-array-and-lowest-possible-price&#34;&gt;&lt;strong&gt;Step 2: Load Your New Volume Pricing Array and Lowest Possible Price&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I did this by creating some script tags in the product show page (or wherever you wish to have this price per unit showing) to make the data from the backend available in a JavaScript file that will update the price dynamically as a user adds or subtracts from the desired quantity. I just called the functions I created in the product decorator here and stored the result in variables for the JavaScript file, app/views/product_show_page.html.erb&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;var all_prices = &amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @product.all_prices %&amp;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:#d20;background-color:#fff0f0&#34;&gt;var lowest_discounted_volume_price =&lt;/span&gt; &amp;lt;%= &lt;span style=&#34;color:#33b&#34;&gt;@product&lt;/span&gt;.lowest_discounted_volume_price %&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step3-write-javascript-code-to-handle-quantity-changes&#34;&gt;&lt;strong&gt;Step3: Write JavaScript Code to Handle Quantity Changes&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In your Spree app just follow typical rails protocol and create a new JavaScript file in app/assets/javascripts/volume_pricing.js and of course require it in your manifest file. Here, just plug your variables in and update your view with the change event (I also added keyup so the price changes if/when a user types in a new quantity)&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;function&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:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;.title&amp;#39;&lt;/span&gt;).on(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;keyup change&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(e){
&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;var&lt;/span&gt; qty = &lt;span style=&#34;color:#038&#34;&gt;parseInt&lt;/span&gt;( $(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;).val());
&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;var&lt;/span&gt; prices_array = all_prices;
&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;var&lt;/span&gt; per_shirt = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; per shirt&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;if&lt;/span&gt; (qty &amp;lt;= prices_array.length)
&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:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;span.price.selling&amp;#39;&lt;/span&gt;).text(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$&amp;#39;&lt;/span&gt;+prices_array[qty -&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;] + per_shirt);
&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;else&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:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;span.price.selling&amp;#39;&lt;/span&gt;).text(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$&amp;#39;&lt;/span&gt;+lowest_discounted_volume_price + per_shirt);
&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;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/how-to-dynamically-update-spree/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/2013/11/how-to-dynamically-update-spree/image-2.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;And now you have dynamically updating price based on selected quantity! I hope you have found this informative and useful, thank you for reading.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Buy One Get One Promotion with Spree</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/08/buy-one-get-one-promotion-with-spree/"/>
      <id>https://www.endpointdev.com/blog/2013/08/buy-one-get-one-promotion-with-spree/</id>
      <published>2013-08-07T00:00:00+00:00</published>
      <author>
        <name>Brian Buchalter</name>
      </author>
      <content type="html">
        &lt;p&gt;Implementing a “Buy One, Get One Free” promotion in
Spree requires implementation of a custom promotion action and
appropriate use of existing promotion rules. This article implements
the promotion by automatically adding and removing immutable “get one” line items whose
price is zero and whose quantity always mirrors its paid
“buy one” counterpart. Although written and tested with Spree’s 1-3-stable branch, the core logic of this tutorial will work with any version of Spree.&lt;/p&gt;
&lt;h3 id=&#34;promotion-eligibility&#34;&gt;Promotion Eligibility&lt;/h3&gt;
&lt;p&gt;Begin by creating a new promotion using a meaningful name. Set the
“event name” field to be “Order contents changed”
so the promotion’s actions are updated as the order is updated. Save
this new promotion, so we can then configure Rules and Actions. In
the Rules section, select the “Product(s)” rule and click
Add. Now choose the products you’d like to be eligible for your
promotion. If you’d like to include broader sets such as entire
taxonomies (and have implemented the custom promotion rules to do
so), feel free to use them. When we implement the promotion
action, you’ll be able to make things work.&lt;/p&gt;
&lt;p&gt;You should now have a product rule that selects some subset of
products eligible for your promotion.&lt;/p&gt;
&lt;h3 id=&#34;adding-a-custom-promotion-action&#34;&gt;Adding a Custom Promotion Action&lt;/h3&gt;
&lt;p&gt;We’ll now add a custom promotion action that will do the work of
creating the free line items for each eligible paid line item. Again, this implementation is specifically for the 1-3-stable branch, but the public interface for promotion actions has (amazingly) remained stable, and is supported from 0-7-0-stable all the way through 2-0-stable.&lt;/p&gt;
&lt;p&gt;First,
we begin by doing &lt;a href=&#34;https://web.archive.org/web/20140330103232/http://guides.spreecommerce.com/developer/promotions.html&#34;&gt;the
basic wiring for creating a new promotion action&lt;/a&gt;. As the guide
instructs, create a new promotion class in a 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-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;# app/models/spree/promotion/buy_one_get_one.rb&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BuyOneGetOne&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PromotionAction&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;perform&lt;/span&gt;(options={})
&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;# TODO&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;Then register this new class with Spree using an initializer.&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;# config/initializers/spree.rb&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;Rails&lt;/span&gt;.application.config.spree.promotions.actions &amp;lt;&amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;BuyOneGetOne&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then update your locales to provide translations for the promotion
action’s name and description.&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;# config/locales/en.yml&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:#a60;background-color:#fff0f0&#34;&gt;en&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;spree&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;promotion_action_types&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;buy_one_get_one&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;name&lt;/span&gt;: &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Buy&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;One&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;One&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;description&lt;/span&gt;: &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Adds&lt;/span&gt; free line items of matching quantity of eligible paid line items.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And although the guide doesn’t instruct you to, it seems required
that you add an empty partial to be rendered in the Spree admin when
you select the rule.&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;# app/views/spree/admin/promotions/actions/_buy_one_get_one.html.erb&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;# Empty File. Spree automatically renders the name and description you provide,&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;# but you could expand here if you&amp;#39;d like.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;pre-flight-check&#34;&gt;Pre-flight check&lt;/h4&gt;
&lt;p&gt;Before moving any farther, it’s best to make sure the new
promotion action has been wired up correctly. Restart your
development server so the initializer registers your new promotion
action and refresh your browser. You should now see a “Buy One
Get One” promotion action.&lt;/p&gt;
&lt;h3 id=&#34;buy-one-get-one-logic&#34;&gt;Buy One Get One Logic&lt;/h3&gt;
&lt;p&gt;Now that we’ve got the promotion action wired, we’re ready to
implement the logic needed to create the new line items. Begin by
collecting the order from the options.&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;# app/models/spree/promotion/buy_one_get_one.rb&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BuyOneGetOne&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PromotionAction&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;perform&lt;/span&gt;(options={})
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; order = options[&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 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;Next we need to determine which line items in the order are eligible
for a corresponding free line item. Because line items are for
variants of products, we must collect the variant ids from the
product rule we setup. If you’ve used something other the default
Spree “Product(s)” rule, just make sure you end up with
equivalent output.&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;# app/models/spree/promotion/buy_one_get_one.rb&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BuyOneGetOne&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PromotionAction&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;perform&lt;/span&gt;(options={})
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; order = options[&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;    eligible_variant_ids = get_eligible_variant_ids_from_promo_rule
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; eligible_variant_ids.present?
&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;get_eligible_variant_ids_from_promo_rule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_rule = promotion.promotion_rules.detect &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |rule|
&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;# If not using the Product rule, update this line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      rule.is_a? &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Promotion&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rules&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Product&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; product_rule.present?
&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;    eligible_products = product_rule.products
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; eligible_products.present?
&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;    eligible_products.collect { |&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;| &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;.variant_ids }.flatten.uniq
&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;Now that we’ve got the eligible variant ids from the promotion’s
rule, we’ll identify the line items which have those variants.&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;# app/models/spree/promotion/buy_one_get_one.rb&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BuyOneGetOne&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PromotionAction&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;perform&lt;/span&gt;(options={})
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; order = options[&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;    eligible_variant_ids = get_eligible_variant_ids_from_promo_rule
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; eligible_variant_ids.present?
&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;    order.line_items.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;variant_id&lt;/span&gt;: eligible_variant_ids).each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |li|
&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;#TODO&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:#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;We’re now ready to process eligible line items. There are several
cases we’ll need to implement to have a working promotion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When we find an eligible, paid
line item:
&lt;ul&gt;
&lt;li&gt;if an existing corresponding free line item exists, update quantity to match the paid line item.&lt;/li&gt;
&lt;li&gt;Else, create corresponding free line item with appropriate quantity.&lt;/li&gt;
&lt;li&gt;When we find a Buy One Get One promotion line item:&lt;/li&gt;
&lt;li&gt;If the corresponding paid line item still exists, do nothing.&lt;/li&gt;
&lt;li&gt;Else, destroy the free line item.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These cases handle the creation, updating, and removal of
promotional line items. Let’s translate these cases into a skeleton
of code which can then be implemented incrementally.&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;# app/models/spree/promotion/buy_one_get_one.rb&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BuyOneGetOne&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PromotionAction&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;perform&lt;/span&gt;(options={})
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; order = options[&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;    eligible_variant_ids = get_eligible_variant_ids_from_promo_rule
&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; eligible_variant_ids.present?
&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;    order.line_items.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;variant_id&lt;/span&gt;: eligible_variant_ids).each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |li|
&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; li.price != &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&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;# It&amp;#39;s an eligible variant and it&amp;#39;s price is not zero, so we&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;# found a &amp;#34;buy one&amp;#34; line item.&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;        matching_get_one_line_item = find_matching_get_one_line_item(li)
&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;# Create or update matching promo line item.&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; matching_get_one_line_item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          matching_get_one_line_item.update_attribute(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:quantity&lt;/span&gt;, li.quantity)
&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;          create_matching_get_one_line_item(li)
&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:#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;        &lt;span style=&#34;color:#888&#34;&gt;# It&amp;#39;s an eligible variant and it&amp;#39;s price is zero, so we&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;# found a &amp;#34;get one&amp;#34; line item.&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;# Verify &amp;#34;buy one&amp;#34; line item still exists, else destroy&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;# the &amp;#34;get one&amp;#34; line item&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        li.destroy &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; find_matching_buy_one_line_item(li)
&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;find_matching_buy_one_line_item&lt;/span&gt;(li)
&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;create_matching_get_one_line_item&lt;/span&gt;(li)
&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;find_matching_get_one_line_item&lt;/span&gt;(li)
&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:#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 well named and commented code reads nicely and clearly covers
the create, update, and destroy cases we need to be concerned with.
Now let’s implement the helper methods.&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&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;find_matching_buy_one_line_item&lt;/span&gt;(get_one_line_item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    get_one_line_item.order.line_items.detect &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |li|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      li.variant_id == get_one_line_item.variant_id &lt;span style=&#34;color:#080&#34;&gt;and&lt;/span&gt; li.price != &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;create_matching_get_one_line_item&lt;/span&gt;(buy_one_line_item)
&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;# You may need to update this with other custom attributes you&amp;#39;ve added.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item = buy_one_line_item.order.line_items.build
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.variant = buy_one_line_item.variant
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.currency = buy_one_line_item.currency
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.price = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.quantity = buy_one_line_item.quantity
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.save
&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;find_matching_get_one_line_item&lt;/span&gt;(buy_one_line_item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buy_one_line_item.order.line_items.detect &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |li|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      li.variant_id == buy_one_line_item.variant_id &lt;span style=&#34;color:#080&#34;&gt;and&lt;/span&gt; li.price != &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&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:#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;We’ve now completed most of the implementation for the promotion
action. Every time the order is updated, all line items are scanned
for eligibility and the appropriate create/update/destroy actions are
taken. This however, isn’t the end of our implementation. Unlike
other items in our cart, we need to prevent users from changing the
quantity of “get one” line items or removing them from the
cart. We also need some way of indicating that these zero price line
items are complements of the Buy One Get One promotion.&lt;/p&gt;
&lt;h3 id=&#34;locked-line-items-with-additional-text&#34;&gt;Locked Line Items with Additional Text&lt;/h3&gt;
&lt;p&gt;While the concept of locked or immutable line items might rightly
deserve its own, separate Spree plugin, we’ll roll a quick one here
to complete the implementation of our promotion. We’ll need to add a
few attributes to the spree_line_items database table, and tweak our
implementation of create_matching_get_one_line_item.&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;# db/migration/add_immutable_and_additional_text_to_spree_line_items.rb&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AddImmutableAndAdditionalTextToSpreeLineItems&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;    add_column &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:spree_line_items&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:immutable&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:boolean&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_column &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:spree_line_items&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:additional_text&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#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;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:#888&#34;&gt;# app/models/spree/promotion/buy_one_get_one.rb&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;create_matching_get_one_line_item&lt;/span&gt;(buy_one_line_item)
&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;# You may need to update this with other custom attributes you&amp;#39;ve added.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item = buy_one_line_item.order.line_items.build
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.variant = buy_one_line_item.variant
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.currency = buy_one_line_item.currency
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.price = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_line_item.quantity = buy_one_line_item.quantity
&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;    new_line_item.immutable = &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;    new_line_item.additional_text = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Buy One Get One&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;    new_line_item.save
&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;Now that we know which line items aren’t meant to be edited by users,
we can update our UI to not render the options to remove immutable
line items or update their quantity. We can also display the
additional text in the line line item when it’s available.&lt;/p&gt;
&lt;p&gt;Missing from this implementation is a way to secure the immutable
line items from manipulation of the immutable line items POSTed
parameters. While this might be required in other cases using
immutable line items, because our promotion action sets the “get
one” line items quantity with every order update, we don’t need
to worry about this issue in this case.&lt;/p&gt;
&lt;h3 id=&#34;test-driving&#34;&gt;Test Driving&lt;/h3&gt;
&lt;p&gt;At this point, you can begin manual testing of your
implementation, but of course automated testing is best. Ideally, we
would have TDDed this against a failing integration test, but the
testing infrastructural setup required to do this is beyond the scope
of the article. What’s worth sharing though, is the syntax of the
assertions I’ve developed to inspect line items, so that you can
implement something similar for your specific needs. Here’s a snippet
from an integration test to give you a sense of the DSL we’ve built
up.&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;# spec/requests/buy_one_get_one_spec.rb&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;context &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;add eligible item to cart&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;  before &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:#888&#34;&gt;# setup cart&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    visit_product_and_add_to_cart eligible_product
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    visit &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/cart&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;should add an identical, immutable, free item&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;    assert_line_item_count(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    assert_line_item_present(eligible_product.name, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;unit_price&lt;/span&gt;: undershirt_amount, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;unit_price&lt;/span&gt;: eligible_product.price, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;immutable&lt;/span&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;    assert_line_item_present(eligible_product.name + &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34; - (Buy One Get One)&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;unit_price&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;immutable&lt;/span&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;    assert_order_total(eligible_product.price)
&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;Of course you’d want to test all the cases we’ve implemented, but
what’s worth focusing on is the ability to assert specific attributes
across many different line items. This is an extremely reusable tool
to have in your testing suite. Good luck implementing!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Little Spree Big Performance Problems</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/08/little-spree-big-performance-problems/"/>
      <id>https://www.endpointdev.com/blog/2013/08/little-spree-big-performance-problems/</id>
      <published>2013-08-05T00:00:00+00:00</published>
      <author>
        <name>Marina Lohova</name>
      </author>
      <content type="html">
        &lt;p&gt;Recently I worked on an online food store serving an area with very little infrastructure. As a result, the orders tended to be really big with lots of products.&lt;/p&gt;
&lt;p&gt;The website worked in the following environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ruby 1.9.2&lt;/li&gt;
&lt;li&gt;Spree 0.60&lt;/li&gt;
&lt;li&gt;Heroku, Bamboo stack&lt;/li&gt;
&lt;li&gt;PostgreSQL 9.2.4&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;h12-timeout-errors&#34;&gt;H12 timeout errors&lt;/h3&gt;
&lt;p&gt;The performance problems started when we migrated Bamboo to Cedar on Heroku and replaced Thin webserver with Unicorn. We started getting a lot of &lt;a href=&#34;https://devcenter.heroku.com/articles/request-timeout&#34;&gt;Heroku Request timeout errors - H12&lt;/a&gt;:&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-0.png&#34;/&gt;
&lt;p&gt;The problems happened mostly when logging in to admin dashboard or during the checkout for the certain orders. H12 errors occur when a HTTP request takes longer than 30 seconds to complete. For example, if a Rails app takes 35 seconds to render the page, the HTTP router returns a 503 after 30 seconds and abandons the incomplete Rails request for good. The Rails request will keep working and logging the normal errorless execution. After completion, the request will indefinitely hang in the application dyno.&lt;/p&gt;
&lt;p&gt;We started debugging H12: we set Unicorn timeout to 20 seconds to prevent the runaway requests and installed the rack-timeout gem with the timeout of 10 seconds to raise an error on a slow request. It all came down to a trivial database timeout!&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-1.png&#34;/&gt;
&lt;p&gt;The application source has not changed during the transition from Bamboo to Cedar, but apparently Cedar/Unicorn is much more sensitive to the troubled code. Below is the list of performance bottlenecks and solutions in Spree. Some of them exist in version 0.60 only, but a lot of them are still present in Spree 1.x, which means that your application may have them too.&lt;/p&gt;
&lt;h3 id=&#34;issue-1-real-time-reports&#34;&gt;Issue #1: Real-time reports&lt;/h3&gt;
&lt;p&gt;Let’s take a closer look at the earlier database timeout code. It came from Admin Dashboard and admin/overview_controller.rb.&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-2.png&#34;/&gt;
&lt;p&gt;The “Best Selling variants&amp;quot; report was being calculated real-time right in the web process:&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;best_selling_variants&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  li = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;LineItem&lt;/span&gt;.includes(&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;      where(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;orders.state = &amp;#39;complete&amp;#39;&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      sum(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:quantity&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:group&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:limit&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&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;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Record counts in the database are large enough to crash the application on Heroku with the database timeout:&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;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;001&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;LineItem&lt;/span&gt;.count
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;162279&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;002&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.count
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;13027&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;003&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Variant&lt;/span&gt;.count
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;14418&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Other reports on the Dashboard experience the same problem. They would cause the timeout in turns when logging into Admin:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;top_grossing_variants&lt;/li&gt;
&lt;li&gt;best_selling_taxons&lt;/li&gt;
&lt;li&gt;last_five_orders&lt;/li&gt;
&lt;li&gt;biggest_spenders&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;
&lt;p&gt;Fortunately, in Spree 1.x the internal reporting system has been replaced with &lt;a href=&#34;https://web.archive.org/web/20130807090154/http://jirafe.com:80/&#34;&gt;Jirafe&lt;/a&gt;:&lt;/p&gt;
&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-3.png&#34;/&gt;
&lt;p&gt;If switching to Spree 1.x is not an option, another way is to move the calculation into a background job, using, for example, delayed_job gem and Heroku Scheduler Addon:&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;task &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:statistics&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:environment&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;Delayed&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Job&lt;/span&gt;.enqueue &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;StatisticsJob&lt;/span&gt;.new
&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;issue-2-large-numbers&#34;&gt;Issue #2: Large numbers&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-4.jpeg&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; /&gt;&lt;/div&gt;
&lt;p&gt;It is an established fact that humans eat a lot! Think about an order of a thousand Heineken 6-pack cans&amp;hellip;&lt;/p&gt;
&lt;p&gt;Or even something like this:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-5.png&#34;/&gt;&lt;/div&gt;
&lt;p&gt;or this:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-6.jpeg&#34;/&gt;&lt;/div&gt;
&lt;p&gt;Spree, both 0.60 and 1.x, proved to have a huge problem if an order has a lot of line items and/or a large quantity of single line items. The potentially dangerous code can be found all over the place. Consider the following 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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;InventoryUnit&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 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;destroy_units&lt;/span&gt;(order, variant, quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    variant_units = order.inventory_units.group_by(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt;)[variant.id].sort_by(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    quantity.ceil.times &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;      inventory_unit = variant_units.shift
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      inventory_unit.destroy
&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;Now imagine what will happen with the order from the first screenshot. We have 15100 inventory units for that one. They will be meticulously destroyed from the inventory one by one in a loop after the checkout. This method was born to crash the application!&lt;/p&gt;
&lt;h3 id=&#34;solution-1&#34;&gt;Solution&lt;/h3&gt;
&lt;p&gt;A simple mindful refactoring was enough to solve the problem for me. There is no need to call “destroy” in the loop for every single inventory unit because we can use the efficient “destroy_all” method. I’m sure this can be optimized further, but it was enough to get rid of the timeout:&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:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;destroy_units&lt;/span&gt;(order, variant, quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  variant_units = order.inventory_units.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    group_by(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt;)[variant.id].
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sort_by(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  variant_units = variant_units.shift(quantity.ceil)
&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;InventoryUnit&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order_id&lt;/span&gt; =&amp;gt; order.id,&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant_id&lt;/span&gt; =&amp;gt; variant.id).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    order(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;state asc&amp;#39;&lt;/span&gt;).limit(quantity.ceil).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    destroy_all
&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;issue-3-real-time-emails&#34;&gt;Issue #3: Real-time emails&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-4.jpeg&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; /&gt;&lt;/div&gt;
&lt;p&gt;All emails in Spree are sent in real-time.&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;  &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;finalize!&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;OrderMailer&lt;/span&gt;.confirm_email(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;).deliver
&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;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;Why is it bad? Let’s look at the following example from my application:&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;confirm_email&lt;/span&gt;(order)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  attachments[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;invoice.pdf&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:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;application/pdf&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:#a60;background-color:#fff0f0&#34;&gt;:content&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;OrderInvoice&lt;/span&gt;.new.to_pdf(order)}
&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;  mail(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:subject&lt;/span&gt;  =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Order #&amp;#39;&lt;/span&gt; + order.number,
&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;:from&lt;/span&gt;   =&amp;gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order_from&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;:to&lt;/span&gt; =&amp;gt; order.email)
&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;In my application “confirm_email” was overridden and generated the PDF invoice. The invoice listed all the products in the order and had about 200 lines in it. Again, it lead to the H12 timeout error.&lt;/p&gt;
&lt;h3 id=&#34;solution-2&#34;&gt;Solution&lt;/h3&gt;
&lt;p&gt;All emails should be sent in the background rather than in the web request. First, because network operations can take a long time, and second, because generating an email can also be slow. For example, sending in the background can be accomplished using &lt;a href=&#34;https://github.com/collectiveidea/delayed_job&#34;&gt;delayed_job gem&lt;/a&gt;:&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;OrderMailer&lt;/span&gt;.delay.confirm_email(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;issue-4-lazy-loading&#34;&gt;Issue #4: Lazy-loading&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-4.jpeg&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; /&gt;&lt;/div&gt;
&lt;p&gt;Ecommerce objects are usually complicated with a lot of associations. This is totally fine as long as you eager-load the associations that will be used most with the loaded object later on. In most cases, Spree does not preload associations for its orders. For example, in spree/base_controller.rb:&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:#33b&#34;&gt;@order&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.find_by_number! params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order_id&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the result, here is what I see in server console while loading the order display page on the frontend:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-9.png&#34;/&gt;&lt;/div&gt;
&lt;p&gt;&amp;hellip;And five more screens like this! The associations of the order — line items, variants and products — generate an additional chain of queries to the database during lazy-loading.&lt;/p&gt;
&lt;h3 id=&#34;solution-eager-loading&#34;&gt;Solution: Eager-loading&lt;/h3&gt;
&lt;p&gt;If I modify the line from the controller like this&amp;hellip;&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:#33b&#34;&gt;@order&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Order&lt;/span&gt;.where(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:number&lt;/span&gt; =&amp;gt; params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:order_id&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         .includes(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_items&lt;/span&gt; =&amp;gt; {&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:product&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:taxons&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt; =&amp;gt; [&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:product&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:option_values&lt;/span&gt;]})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         .includes(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adjustments&lt;/span&gt;).first&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&amp;hellip;SQL queries will shrink down to this:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-10.png&#34;/&gt;&lt;/div&gt;
&lt;p&gt;Eager-loading did the trick. Of course, not everything needs to be loaded eagerly, and the solution varies from case to case. It worked like charm in my case.&lt;/p&gt;
&lt;h3 id=&#34;issue-5-dangerous-code-all-over-the-place&#34;&gt;Issue #5: Dangerous code all over the place&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-4.jpeg&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; /&gt;&lt;/div&gt;
&lt;p&gt;There a lot of places in the Spree source code that are not optimized for performance. We don’t need to look far for an example, because there is another killer method right near the &amp;ldquo;destroy_units&amp;rdquo; one we inspected earlier!&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;InventoryUnit&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 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;create_units&lt;/span&gt;(order, variant, sold, back_order)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shipment = order.shipments.detect {|shipment| !shipment.shipped? }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sold.ceil.times {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      order.inventory_units.create(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt; =&amp;gt; variant, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:state&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sold&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipment&lt;/span&gt; =&amp;gt; shipment)
&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;    back_order.ceil.times {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      order.inventory_units.create(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variant&lt;/span&gt; =&amp;gt; variant, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:state&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;backordered&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:shipment&lt;/span&gt; =&amp;gt; shipment)
&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;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;And then:&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;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;  back_order = determine_backorder(order, variant, quantity)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sold = quantity - back_order
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  create_units(order, line_item.variant,  sold, back_order)
&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;I received a timeout either in the “sold” or the “backorder” loop, but there wasn’t a time when I didn’t receive a timeout!&lt;/p&gt;
&lt;h3 id=&#34;solution-3&#34;&gt;Solution&lt;/h3&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;create_units&lt;/span&gt;(order, variant, sold, back_order)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  shipment = order.shipments.detect {|shipment| !shipment.shipped? }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  values = sold.ceil.times.to_a.map { &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;order.id&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;variant.id&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;#39;sold&amp;#39;,&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;shipment.id&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;    back_order.ceil.times.to_a.map { &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;order.id&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;variant.id&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;#39;backordered&amp;#39;,&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;shipment.id&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;  values.in_groups_of(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;500&lt;/span&gt;, &lt;span style=&#34;color:#080&#34;&gt;false&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |group|
&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;InventoryUnit&lt;/span&gt;.connection.execute(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;INSERT INTO inventory_units(order_id, variant_id, state, shipment_id) VALUES &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;group.join(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;,&amp;#39;&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;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;Another example: every line item has the after_create and after_save callbacks. The callback invokes the  Order.update! method. Order.update! method calls update_totals method&amp;hellip;twice throughout the 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;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;update_totals&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;.payment_total = payments.completed.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt;).sum
&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;.item_total = line_items.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt;).sum
&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;.adjustment_total = adjustments.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt;).sum
&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;.total = item_total + adjustment_total
&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;Now imagine the order from the second screenshot with a lot of line items. During the checkout “update_totals” will be called each time the line item is saved. Typically, this line would produce a timeout, because the  “line_items” association was, of course, not preloaded:&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;line_items.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:amount&lt;/span&gt;).sum&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I couldn’t list every circumstance like that, because it would require a lot of context to explain the catch, but I can still say many times …&lt;/p&gt;
&lt;h3 id=&#34;no-long-running-tasks-in-the-web-request&#34;&gt;No long-running tasks in the web request!&lt;/h3&gt;
&lt;p&gt;Be it Spree, Heroku, or any other context, environment or platform, please, never do the following in the web process:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/08/little-spree-big-performance-problems/image-12.png&#34; style=&#34;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&#34; /&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;(!!!) Heavy database usage (slow or numerous queries, N+1 queries)&lt;/li&gt;
&lt;li&gt;Sending an email&lt;/li&gt;
&lt;li&gt;Accessing a remote API (posting to Twitter, querying Flickr, etc.)&lt;/li&gt;
&lt;li&gt;Rendering an image or PDF&lt;/li&gt;
&lt;li&gt;Heavy computation (computing a fibonacci sequence, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Say “No” to all these things to ensure a much happier life for your application!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree’s New Release Policy</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/06/sprees-new-release-policy/"/>
      <id>https://www.endpointdev.com/blog/2013/06/sprees-new-release-policy/</id>
      <published>2013-06-17T00:00:00+00:00</published>
      <author>
        <name>Brian Buchalter</name>
      </author>
      <content type="html">
        &lt;p&gt;Spree has recently updated its documentation regarding contributions and has included (maybe for the first time?) an official &lt;a href=&#34;https://web.archive.org/web/20130608210307/http://guides.spreecommerce.com/developer/contributing.html&#34;&gt;Release Policy&lt;/a&gt;. This is an important step forward for the Spree community so that developers can communicate to clients the potential costs and benefits when upgrading Spree, understand how well Spree supports older releases, and gauge the overall speed of the “upgrade treadmill”.&lt;/p&gt;
&lt;h3 id=&#34;deprecation-warnings&#34;&gt;Deprecation Warnings&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Deprecation warnings are to be added in patch releases (i.e. 2.0.2) and the code being deprecated will only be removed in minor versions. For example, if a deprecation warning is added in 2.0.2, 2.0.3 will still contain the same deprecation warning but in 2.1.0 the deprecation warning and the code will be gone.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Deprecation warnings are very helpful for developers, but without a robust test suite exercising your application, it’s easy for deprecation warnings to go unnoticed. A strong test suite coupled with deprecation warnings helps you manage your client’s expectations about how upgrades can affect your Spree customizations and extensions.&lt;/p&gt;
&lt;h3 id=&#34;master-branch-receives-all-patches&#34;&gt;Master Branch Receives All Patches&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Master branch receives all patches, including new features and breaking API changes (with deprecation warnings, if necessary).&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;No surprises here; the master branch is for developers and should not be used for production. If you think you’ve encountered a bug in Spree, make sure to create a failing test against master to make sure it hasn’t be resolved by any existing patches. Read more about &lt;a href=&#34;https://web.archive.org/web/20130608210307/http://guides.spreecommerce.com/developer/contributing.html&#34;&gt;filing an issue&lt;/a&gt; for additional details.&lt;/p&gt;
&lt;h3 id=&#34;current-stable-release-branch&#34;&gt;Current Stable Release Branch&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;One branch “back” from master (currently 2-0-stable) receives patches that fix all bugs, and security issues, and modifications for recently added features (for example, split shipments). Breaking API changes should be avoided, but if unavoidable then a deprecation warning MUST be provided before that change takes place.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Continuing Spree’s history of very aggressive refactoring, breaking API changes are permitted in the current stable branch. If you’re looking for a truly stable release of Spree, you’ll need to look back two stable branches behind master.&lt;/p&gt;
&lt;h3 id=&#34;two-releases-behind-master&#34;&gt;Two Releases Behind Master&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Two branches “back” from master (currently 1-3-stable) receives patches for major and minor issues, and security problems. Absolutely no new features, tweaking or API changes.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In my opinion, this is the only branch that should be considered for use in production. With the API locked down and a greater chance of most bugs worked out while it was the current release branch, the “two-back” stable branch is a safe bet that’s going to have the most up-to-date feature set.&lt;/p&gt;
&lt;h3 id=&#34;three-and-four-releases-behind-master&#34;&gt;Three and Four Releases Behind Master&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Three branches back from master (currently 1-2-stable) receives patches for major issues and security problems. The severity of an issue will be determined by the person investigating the issue. Absolutely no features, tweaking or API changes. Four branches and more “back” from master (currently 1-1-stable and lesser) receive patches only for security issues, as people are still using these branches and may not be able to or wish to upgrade to the latest version of Spree.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It’s nice to see a fairly strong commitment to accepting security patches, although if we look at this in absolute terms, the 1.1.x release was put into security-patches-only mode after just 13 months. Considering that the 1-1-stable branch is 2,127 commits behind the 1-3-stable branch (!!), it’s clear that Spree is now formalizing it’s very aggressive release culture.&lt;/p&gt;
&lt;h3 id=&#34;managing-the-upgrade-treadmill&#34;&gt;Managing the Upgrade Treadmill&lt;/h3&gt;
&lt;p&gt;As stated previously, a strong test suite is the best tool available to be able to determine what upstream updates affect your Spree customizations. Coupled with deprecation warnings, it becomes a fairly straight-forward process for identifying breaking changes, creating estimates for fixes, and communicating these costs to clients. Following the stated guides for &lt;a href=&#34;https://web.archive.org/web/20130708010221/http://guides.spreecommerce.com/developer/authentication.html&#34;&gt;customizing Spree&lt;/a&gt; is also recommended. When visiting the Spree guides section on customization, you’ll first be taken to the sub-section for authentication. Make sure to expand the Customization menu on your left to see additional guidance for customizing &lt;a href=&#34;https://web.archive.org/web/20130715044956/http://guides.spreecommerce.com/developer/i18n.html&#34;&gt;internationalization&lt;/a&gt;, &lt;a href=&#34;https://web.archive.org/web/20130709005650/http://guides.spreecommerce.com/developer/view.html&#34;&gt;views&lt;/a&gt;, &lt;a href=&#34;https://web.archive.org/web/20130610052811/http://guides.spreecommerce.com/developer/asset.html&#34;&gt;JavaScript, stylesheet and image assets&lt;/a&gt;, &lt;a href=&#34;https://web.archive.org/web/20160322070928/http://guides.spreecommerce.com:80/developer/logic.html&#34;&gt;business logic&lt;/a&gt;, and &lt;a href=&#34;https://web.archive.org/web/20130715001359/http://guides.spreecommerce.com/developer/checkout.html&#34;&gt;checkout&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another important suggestion helpfully submitted by my colleague Mike Farmer was to never simply add gem ‘spree’ to your Gemfile. You should be very conscious about what version of Spree you want to use, and make sure you specify it in your Gemfile.&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>Developing a Spree Application</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/developing-spree-application/"/>
      <id>https://www.endpointdev.com/blog/2012/06/developing-spree-application/</id>
      <published>2012-06-15T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;/blog/2012/06/developing-spree-application/image-0-big.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; height=&#34;320&#34; src=&#34;/blog/2012/06/developing-spree-application/image-0.jpeg&#34; width=&#34;300&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mike Farmer presented one of the projects he worked on. He gave us a very detailed overview of integration of a Spree application into Facebook for a large client.&lt;/p&gt;
&lt;p&gt;He also described the changes that were made in the Spree project to customize it to the client’s needs.&lt;/p&gt;
&lt;p&gt;The most interesting thing in this Spree shop usage is that the company’s clients make extensive use of the admin panel and they can sell their own products for other clients.&lt;/p&gt;
&lt;p&gt;Mike showed us a great overview of writing better Rails applications using better object oriented code along with TDD and extensive SASS usage. He also described the great tools he uses including Screen and Vagrant.&lt;/p&gt;
&lt;p&gt;Mike also talked about the things he learned during working on this project, especially about Spree and Ruby.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Multi-store Architecture for Ecommerce</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/02/multi-store-architecture-ecommerce/"/>
      <id>https://www.endpointdev.com/blog/2012/02/multi-store-architecture-ecommerce/</id>
      <published>2012-02-29T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Something that never seems to go out of style in ecommerce development is the request for multi-site or multi-store architecture running on a given platform. Usually there is interest in this type of setup to encourage build-out and branding of unique stores that have shared functionality.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.backcountry.com/&#34;&gt;&lt;img src=&#34;/blog/2012/02/multi-store-architecture-ecommerce/image-0.gif&#34; style=&#34;padding-right: 10px&#34; width=&#34;250&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.steepandcheap.com/&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;http://www.steepandcheap.com/images/steepcheap/header/logo.png&#34; width=&#34;200&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;img src=&#34;/blog/2012/02/multi-store-architecture-ecommerce/image-1.gif&#34; style=&#34;padding-left: 10px&#34; width=&#34;200&#34;/&gt;
&lt;p&gt;A few of Backcountry.com’s stores driven by a multi-store architecture, developed with End Point support.&lt;/p&gt;
&lt;p&gt;End Point has developed several multi-store architectures on open source ecommerce platforms, including &lt;a href=&#34;http://www.backcountry.com/&#34;&gt;Backcountry.com&lt;/a&gt; (Interchange/Perl), &lt;a href=&#34;http://www.collegedistrict.com/&#34;&gt;College District&lt;/a&gt; (Interchange/Perl), and Fantegrate (Spree/Rails). Here’s an outline of several approaches and the advantages and disadvantages for each method.&lt;/p&gt;
&lt;h3 id=&#34;option-1-copy-of-code-base-and-database-for-every-site&#34;&gt;Option #1: Copy of Code Base and Database for Every Site&lt;/h3&gt;
&lt;p&gt;This option requires multiple copies of the ecommerce platform code base, and multiple database instances connected to each code base. The stores could even be installed on different servers. This solution isn’t a true multi-store architecture, but it’s certainly the first stop for a quick and dirty approach to deploy multiple stores.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;advantages&lt;/strong&gt; to this method are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Special template logic doesn’t have to be written per site — the templates would simply follow the ecommerce platform’s template pattern.&lt;/li&gt;
&lt;li&gt;Relative to Option #3 described below, no custom database development is required.&lt;/li&gt;
&lt;li&gt;Custom business logic may be more easily applied to a set of the stores, without affecting the other stores.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;disadvantages&lt;/strong&gt; to this method are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maintenance of the applications can be time consuming, as changes must be applied to all instances.&lt;/li&gt;
&lt;li&gt;Custom changes must be applied to all multi-store instances.&lt;/li&gt;
&lt;li&gt;Users and administrator accounts are not shared across multiple stores.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;option-2-single-code-base-single-database&#34;&gt;Option #2: Single Code Base, Single Database&lt;/h3&gt;
&lt;img src=&#34;/blog/2012/02/multi-store-architecture-ecommerce/image-2.png&#34; style=&#34;padding: 10px 0 0 10px; float: right;&#34;/&gt;
&lt;p&gt;In this method, there is one copy of the source code that interacts with one database. The single database would be modified to contain a store specific id per product, order, and peripheral tables. The code base would also have to be modified to be able to limit the visible products described &lt;a href=&#34;/blog/2010/05/spree-multi-store-architecture/&#34;&gt;here&lt;/a&gt;. In this method, the individual store may be identified by the domain or subdomain. With this method, there may also be code customization that allows for custom templates per store.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;advantages&lt;/strong&gt; to this method are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relative to Option #1, maintenance for one code base is relatively simple.&lt;/li&gt;
&lt;li&gt;User and administrator accounts are shared across multiple stores.&lt;/li&gt;
&lt;li&gt;Super administrators may view and manage data from one backend administrative interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;disadvantages&lt;/strong&gt; to this method are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rights and role management can be complicated.&lt;/li&gt;
&lt;li&gt;Development is required for code and database customization.&lt;/li&gt;
&lt;li&gt;Development is required for coding to handle flexible templating across stores.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A second option in multi-store architecture may use a data model with store specific entries in various tables, described &lt;a href=&#34;/blog/2010/05/spree-multi-store-architecture/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;option-3-single-code-base-single-database-with-schemas-or-views-per-store&#34;&gt;Option #3: Single Code Base, Single Database with Schemas or Views Per Store&lt;/h3&gt;
&lt;p&gt;In this method, there is one copy of the source code that interacts with a database that has views specific to that store, or a schema specific to that store. In this case, the code base would not necessarily need customization since the data model it accesses should follow the conventions of the ecommerce platform. However, moderate database customization is required in this method. With this method, there may also be code customization that allows for custom templates per store.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;advantages&lt;/strong&gt; to this method are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relative to Option #1, maintenance for one code base is relatively simple.&lt;/li&gt;
&lt;li&gt;Relative to option #2, code base changes are minimal.&lt;/li&gt;
&lt;li&gt;User accounts may or may not be shared across stores.&lt;/li&gt;
&lt;li&gt;Relative to option #2, there may be a potential performance gain by removing time spent limiting data to the current store instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;disadvantage&lt;/strong&gt; to this method is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customization and development is required for database configuration and management of multi-store database schemas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A tangential variation on the methods above are two different codebases and functionality attached to one back-end web service and backing database, such as the architecture we implemented for Locate Express. And a similar tangential variation I’ve investigated before is one that might use a &lt;a href=&#34;/blog/2011/03/ecommerce-sinatra-shopping-cart/&#34;&gt;Sinatra driven front-end&lt;/a&gt; and a Rails backed admin, such as &lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;RailsAdmin&lt;/a&gt; used in &lt;a href=&#34;/blog/2012/01/piggybak-mountable-ecommerce-ruby-on/&#34;&gt;Piggybak&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.collegedistrict.com/&#34;&gt;&lt;img src=&#34;/blog/2012/02/multi-store-architecture-ecommerce/image-3.png&#34; width=&#34;700&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;College District has a collection of stores driven by a multi-store architecture, developed with End Point support.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In most cases for our clients, there is cost-benefit analysis that drives the decision between the three options described above. Option #1 might be an acceptable solution for someone interested in building out two or three stores, but the latter two options would be more suitable for someone interested in spinning up many additional instances quickly with lower long term maintenance costs.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Rails Request-Based Routing Constraints in Spree</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/12/rails-spree-routing-constraint/"/>
      <id>https://www.endpointdev.com/blog/2011/12/rails-spree-routing-constraint/</id>
      <published>2011-12-21T00:00:00+00:00</published>
      <author>
        <name>Brian Gadoury</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently adopted an unreleased ecommerce project running Spree 0.60.0 on Rails 3.0.9. The site used a Rails routing constraint and wildcard DNS to dynamically route subdomains to the “dispatch” action of the organizations_controller. If a request’s subdomain component matched that regular expression, it was routed to the dispatch method. Here’s the original route:&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;match &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;organizations#dispatch&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:constraints&lt;/span&gt; =&amp;gt; { &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:subdomain&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/.+/&lt;/span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The business requirement driving this feature was that a User could register an Organization by submitting a form on the site. Once that Organization was marked “approved” by an admin, that Organization would become accessible at their own subdomain—​&lt;em&gt;no server configuration required&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For marketing reasons, we decided to switch from subdomains to top-level subdirectories. This meant RESTful routes (e.g. domain.com/organizations/143) wouldn’t cut it. In order to handle this, I created a routing constraint class called OrgConstraint. This routing constraint class works in tandem with a tweaked version of that original route.&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;match &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;*org_url&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;organizations#show&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:constraints&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;OrgConstraint&lt;/span&gt;.new&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The :constraints param takes an &lt;em&gt;instance&lt;/em&gt; of a class (not a class name) that responds to a matches? predicate method that returns true or false. If matches? returns true, the request will be routed to that controller#action. Else, that route is treated like any other non-matching route. Here’s the entire OrgConstraint 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;OrgConstraint&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;matches?&lt;/span&gt;(request)
&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;Organization&lt;/span&gt;.valid_url? request.path_parameters[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:org_url&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 how Rails automatically passes the &lt;a href=&#34;http://guides.rubyonrails.org/action_controller_overview.html#the-request-object&#34;&gt;request object&lt;/a&gt; to the matches? method. Also note how the relative url of the request is available via the :org_url symbol-​the same identifier we used in the route definition. The Organization.valid_url? class method encapsulates the logic of examining a simple cached (via Rails.cache) hash consisting of organization urls as keys and true as their value.&lt;/p&gt;
&lt;p&gt;The final step in this process is, of course, the organizations_controller’s show method. It now needs to look for that same :org_url param that the route definition creates, in the standard params hash we all know and love:&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;show&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:#33b&#34;&gt;@organization&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Organization&lt;/span&gt;.find(params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:id&lt;/span&gt;]) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; 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 style=&#34;color:#888&#34;&gt;# from routing constraint&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:#33b&#34;&gt;@organization&lt;/span&gt; ||= &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Organization&lt;/span&gt;.find_by_url(params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:org_url&lt;/span&gt;]) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:org_url&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;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I should point out that Rails instantiates exactly one instance of your routing constraint class when it first loads your routes. This means you’ll want to ensure your class’s design will respond appropriately to changes in any underlying data. This is one of the reasons the Organization class caches the {$org_url =&amp;gt; true} hash rather than using instance variables within the OrgConstraint class.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Paperclip in Spree: Extending Product Image Sizes</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/06/paperclip-spree-overriding-product/"/>
      <id>https://www.endpointdev.com/blog/2011/06/paperclip-spree-overriding-product/</id>
      <published>2011-06-06T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Spree uses the popular gem &lt;a href=&#34;https://github.com/thoughtbot/paperclip&#34;&gt;Paperclip&lt;/a&gt; for assigning images as attachments to products. The basic installation requires you to install the gem, create a migration to store the paperclip-specific fields in your model, add the &lt;strong&gt;has_attached_file&lt;/strong&gt; information to the model with the attachment, add the ability to upload the file, and display the file in a view. In Spree, the Image model has an attached file with the following properties:&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;Image&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Asset&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;  has_attached_file &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:attachment&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;:styles&lt;/span&gt; =&amp;gt; { &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:mini&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;48x48&amp;gt;&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:#a60;background-color:#fff0f0&#34;&gt;:small&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;100x100&amp;gt;&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:#a60;background-color:#fff0f0&#34;&gt;:product&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;240x240&amp;gt;&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:#a60;background-color:#fff0f0&#34;&gt;:large&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;600x600&amp;gt;&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:#a60;background-color:#fff0f0&#34;&gt;:default_style&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:product&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;/assets/products/:id/:style/: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;:path&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;:rails_root/public/assets/products/:id/:style/: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&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;As you can see, when an admin uploads an image, four image sizes are created: large, product, small, and mini.&lt;/p&gt;
&lt;p&gt;
&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5615185163090719650&#34; src=&#34;/blog/2011/06/paperclip-spree-overriding-product/image-0.jpeg&#34; style=&#34;width: 160px;&#34;/&gt;
&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5615185163090719650&#34; src=&#34;/blog/2011/06/paperclip-spree-overriding-product/image-0.jpeg&#34; style=&#34;width: 120px;&#34;/&gt;
&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5615185163090719650&#34; src=&#34;/blog/2011/06/paperclip-spree-overriding-product/image-0.jpeg&#34; style=&#34;width: 100px;&#34;/&gt;
&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5615185163090719650&#34; src=&#34;/blog/2011/06/paperclip-spree-overriding-product/image-0.jpeg&#34; style=&#34;width: 48px;&#34;/&gt;&lt;br&gt;
Four images are created per product image uploaded in Spree (Note: not to scale).
&lt;/p&gt;
&lt;p&gt;Last week, I wanted to add several additional sizes to be created upon upload to improve performance. This involved several steps, described below.&lt;/p&gt;
&lt;h3 id=&#34;step-1-extend-attachment_definitions&#34;&gt;Step 1: Extend attachment_definitions&lt;/h3&gt;
&lt;p&gt;First, I had to override the image attachment styles, with the code shown below. My application is running on Spree 0.11.2 (Rails 2.3.*), so this was added inside the extension activate method, but in Rails 3.0 versions of Spree, this would be added inside the engine’s activate 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:#036;font-weight:bold&#34;&gt;Image&lt;/span&gt;.attachment_definitions[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:attachment&lt;/span&gt;][&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:styles&lt;/span&gt;].merge!(
&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;:newsize1&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;200x200&amp;gt;&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:#a60;background-color:#fff0f0&#34;&gt;:newsize2&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;284x284&amp;gt;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-2-add-image-helper-methods&#34;&gt;Step 2: Add Image Helper Methods&lt;/h3&gt;
&lt;p&gt;Spree has the following bit of code in its base_helper.rb, which in theory should create methods for calling each image (mini_image, small_image, product_image, large_image, newsize1_image, newsize2_image):&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;Image&lt;/span&gt;.attachment_definitions[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:attachment&lt;/span&gt;][&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:styles&lt;/span&gt;].each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |style, v|
&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;style&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;_image&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |product, *options|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      options = options.first || {}
&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; product.images.empty?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        image_tag &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;noimage/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;style&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;.jpg&amp;#34;&lt;/span&gt;, options
&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;        image = product.images.first
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options.reverse_merge! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:alt&lt;/span&gt; =&amp;gt; image.alt.blank? ? product.name : image.alt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        image_tag image.attachment.url(style), options
&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;But for some reason in this application, perhaps based on order of extension evaluation, this was only applied to the original image sizes. I remedied this by adding the following code to my extension base helper:&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:#a60;background-color:#fff0f0&#34;&gt;:newsize1&lt;/span&gt;, newsize2].each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |style|
&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;style&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;_image&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |product, *options|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      options = options.first || {}
&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; product.images.empty?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        image_tag &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;noimage/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;style&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;.jpg&amp;#34;&lt;/span&gt;, options
&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;        image = product.images.first
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options.reverse_merge! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:alt&lt;/span&gt; =&amp;gt; image.alt.blank? ? product.name : image.alt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        image_tag image.attachment.url(style), options
&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;step-3-create-cropped-images-for-existing-images&#34;&gt;Step 3: Create Cropped Images for Existing Images&lt;/h3&gt;
&lt;p&gt;Finally, instead of requiring all images to be re-uploaded to create the new cropped images, I wrote a quick bash script to generate images with the new sizes. This script was placed inside the RAILS_ROOT/public/assets/products/ directory, where product images are stored. The script iterates through each existing directory and creates cropped images based on the original uploaded image with the ImageMagick command-line tool, which is what Paperclip uses for resizing.&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:#c00;font-weight:bold&#34;&gt;#!/bin/bash
&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:#c00;font-weight:bold&#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;for&lt;/span&gt; i in &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;ls */original/*&lt;span style=&#34;color:#d20;background-color:#fff0f0&#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;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:#369&#34;&gt;image_name&lt;/span&gt;=&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;i&lt;/span&gt;#*original&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#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:#369&#34;&gt;dir_name&lt;/span&gt;=&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;i&lt;/span&gt;/&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;original&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$image_name&lt;/span&gt;/&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mkdir &lt;span style=&#34;color:#369&#34;&gt;$dir_name&lt;/span&gt;/newsize1/ &lt;span style=&#34;color:#369&#34;&gt;$dir_name&lt;/span&gt;/newsize2/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    convert &lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt; -resize &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;200x200&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dir_name&lt;/span&gt;/newsize1/&lt;span style=&#34;color:#369&#34;&gt;$image_name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    convert &lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt; -resize &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;284x284&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dir_name&lt;/span&gt;/newsize2/&lt;span style=&#34;color:#369&#34;&gt;$image_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:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;created images for &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$i&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;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-4-update-views&#34;&gt;Step 4: Update Views&lt;/h3&gt;
&lt;p&gt;Finally, I added newsize1_image and newsize2_image methods throughout the views, 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-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;%= link_to newsize1_image(product), product %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;%= link_to newsize2_image(taxon.products.first), seo_url(taxon) %&amp;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;It would be ideal to remove Step 2 described here by investigating why the image methods are not defined by the Spree core BaseHelper module. It’s possible that this is working as expected on more recent versions of Spree. Other than that violation of the DRY principle, it is a fairly simple process to extend the Paperclip image settings to include additional sizes.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Performance Benchmarking</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/05/spree-performance-benchmarking/"/>
      <id>https://www.endpointdev.com/blog/2011/05/spree-performance-benchmarking/</id>
      <published>2011-05-25T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I see a lot of questions regarding Spree performance in the &lt;a href=&#34;https://groups.google.com/forum/#!forum/spree-user&#34;&gt;spree-user group&lt;/a&gt;, but they are rarely answered with metrics. I put together a quick script using the generic benchmark tool &lt;a href=&#34;https://httpd.apache.org/docs/2.0/programs/ab.html&#34;&gt;ab&lt;/a&gt; to review some data. Obviously, the answer to how well a site performs and scales is highly dependent on the host and the consumption of the web application, so the data here needs to be taken with a grain of salt. Another thing to note is that only two of the following use cases are running on Rails 3.0 — many of our current Spree clients are on Spree 0.11.2 or older. I also included one non-Spree Rails ecommerce application, in addition to a few non-Rails applications for comparison. All of the tests were run from my home network, so in theory there shouldn’t be bias on performance tests for sites running on End Point servers.&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&gt;&lt;/td&gt;   &lt;td align=&#34;center&#34; colspan=&#34;4&#34;&gt;ab -n 100&lt;/td&gt;   &lt;td&gt;&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 2 homepage&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 20 homepage&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 2&lt;br/&gt;
product page&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 20&lt;br/&gt;
product page&lt;/td&gt;   &lt;td&gt;&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #1&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: 4 cores, 512 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: &lt;100   &lt;/td&gt;   &lt;td&gt;7.49&lt;/td&gt;   &lt;td&gt;24.75&lt;/td&gt;   &lt;td&gt;6.49&lt;/td&gt;   &lt;td&gt;19.87&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;266.889&lt;/td&gt;&lt;td&gt;808.041&lt;/td&gt;&lt;td&gt;307.997&lt;/td&gt;&lt;td&gt;1006.552&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #2&lt;/b&gt;&lt;br/&gt;
Spree 0.11.2&lt;br/&gt;
Hosting: Engineyard, medium instance&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;5.32&lt;/td&gt;&lt;td&gt;20.28&lt;/td&gt;&lt;td&gt;5.36&lt;/td&gt;&lt;td&gt;18.03&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;375.713&lt;/td&gt;&lt;td&gt;986.309&lt;/td&gt;&lt;td&gt;373.289&lt;/td&gt;&lt;td&gt;1109.524&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #3&lt;/b&gt;&lt;br/&gt;
Spree: 0.9.0&lt;br/&gt;
Hosting: 4 cores, 1 GB RAM&lt;br/&gt;
DB: PostgreSQL&lt;br/&gt;
# Products: &lt;100   &lt;/td&gt;   &lt;td&gt;4.91&lt;/td&gt;&lt;td&gt;25.39&lt;/td&gt;&lt;td&gt;1.98&lt;/td&gt;&lt;td&gt;6.54&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;407.135&lt;/td&gt;&lt;td&gt;787.782&lt;/td&gt;&lt;td&gt;1011.875&lt;/td&gt;&lt;td&gt;3060.062&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;(Former) Client #4&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: Unknown&lt;br/&gt;
DB: PostgreSQL&lt;br/&gt;
# Products: &gt;5000   &lt;/td&gt;   &lt;td&gt;20.69&lt;/td&gt;&lt;td&gt;8.84&lt;/td&gt;&lt;td&gt;10.15&lt;/td&gt;&lt;td&gt;19.28&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;96.673&lt;/td&gt;&lt;td&gt;2262.105&lt;/td&gt;&lt;td&gt;196.996&lt;/td&gt;&lt;td&gt;1037.146&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #5&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: EngineYard, small instance&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 1   &lt;/td&gt;   &lt;td&gt;12.28&lt;/td&gt;&lt;td&gt;16.23&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;162.909&lt;/td&gt;&lt;td&gt;1231.945&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #6&lt;/b&gt;&lt;br/&gt;
Spree: 0.40&lt;br/&gt;
Hosting: 4 cores, 1 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 50-100   &lt;/td&gt;   &lt;td&gt;3.61&lt;/td&gt;&lt;td&gt;8.93&lt;/td&gt;&lt;td&gt;2.96&lt;/td&gt;&lt;td&gt;3.06&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;553.569&lt;/td&gt;&lt;td&gt;2240.657&lt;/td&gt;&lt;td&gt;675.306&lt;/td&gt;&lt;td&gt;6539.433&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;SpreeDemo&lt;/b&gt;&lt;br/&gt;
Spree: Edge&lt;br/&gt;
Hosting: Heroku, 2 dynos&lt;br/&gt;
DB: Unknown&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;8.17&lt;/td&gt;&lt;td&gt;12.79&lt;/td&gt;&lt;td&gt;4.7&lt;/td&gt;&lt;td&gt;5.48&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;244.831&lt;/td&gt;&lt;td&gt;1563.642&lt;/td&gt;&lt;td&gt;425.27&lt;/td&gt;&lt;td&gt;3652.927&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #7&lt;/b&gt;&lt;br/&gt;
*custom Rails ecommerce app&lt;br/&gt;
Hosting: 1.0 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 1000s   &lt;/td&gt;   &lt;td&gt;5.43&lt;/td&gt;&lt;td&gt;29.8&lt;/td&gt;&lt;td&gt;4.45&lt;/td&gt;&lt;td&gt;23.14&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;368.409&lt;/td&gt;&lt;td&gt;671.082&lt;/td&gt;&lt;td&gt;448.962&lt;/td&gt;&lt;td&gt;864.24&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Interchange Demo&lt;/b&gt;&lt;br/&gt;
Hosting: 4 cores, 2 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: &gt;500   &lt;/td&gt;   &lt;td&gt;7.41&lt;/td&gt;&lt;td&gt;55.27&lt;/td&gt;&lt;td&gt;7.5&lt;/td&gt;&lt;td&gt;13.93&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;269.942&lt;/td&gt;&lt;td&gt;361.875&lt;/td&gt;&lt;td&gt;266.492&lt;/td&gt;&lt;td&gt;1435.51&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #8&lt;/b&gt;&lt;br/&gt;
*PHP site,&lt;br/&gt;
serves fully cached pages&lt;br/&gt;
with nginx with no app server or db hits&lt;br/&gt;
Hosting: 4 cores, 4 GB RAM   &lt;/td&gt;   &lt;td&gt;10.81&lt;/td&gt;&lt;td&gt;30.54&lt;/td&gt;&lt;td&gt;6.05&lt;/td&gt;&lt;td&gt;9.87&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;184.994&lt;/td&gt;&lt;td&gt;654.858&lt;/td&gt;&lt;td&gt;330.727&lt;/td&gt;&lt;td&gt;2027.092&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Magento Demo&lt;/b&gt;&lt;br/&gt;
Hosting: Unknown&lt;br/&gt;
DB: Unknown&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;4.26&lt;/td&gt;&lt;td&gt;44.85&lt;/td&gt;&lt;td&gt;2.68&lt;/td&gt;&lt;td&gt;36.29&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;469.831&lt;/td&gt;&lt;td&gt;445.931&lt;/td&gt;&lt;td&gt;745.472&lt;/td&gt;&lt;td&gt;551.11&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Here’s the same data in graphical form:&lt;/p&gt;
&lt;h3 id=&#34;requests-per-second&#34;&gt;Requests per Second&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/05/spree-performance-benchmarking/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5610751500251002546&#34; src=&#34;/blog/2011/05/spree-performance-benchmarking/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 740px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;time-per-request-ms&#34;&gt;Time Per Request (ms)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/05/spree-performance-benchmarking/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5610751511624706978&#34; src=&#34;/blog/2011/05/spree-performance-benchmarking/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 740px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We expect to see high performance on some of the sites with significant performance optimization. On smaller VPS, we expect to see the the server choke with higher concurrency.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>ActiveProduct — Just the Spree Products</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/03/activeproduct-just-spree-products/"/>
      <id>https://www.endpointdev.com/blog/2011/03/activeproduct-just-spree-products/</id>
      <published>2011-03-28T00:00:00+00:00</published>
      <author>
        <name>Sonny Cook</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;activeproduct&#34;&gt;ActiveProduct&lt;/h3&gt;
&lt;p&gt;I wanted to see how difficult it would be to cut out the part of
Spree that makes up the product data model and turn it into a self
sufficient Rails 3 engine. I followed the tutorial &lt;a href=&#34;https://web.archive.org/web/20100727143748/http://www.themodestrubyist.com/2010/03/05/rails-3-plugins---part-2---writing-an-engine/&#34;&gt;here&lt;/a&gt;
to get started with a basic engine plugin. Since it sounded good and
nobody else seems to be using it, I decided to call this endeavor ActiveProduct.&lt;/p&gt;
&lt;h3 id=&#34;which-bits&#34;&gt;Which Bits?&lt;/h3&gt;
&lt;p&gt;The next step was to decide which parts I needed to get. Minimally, I
need the models and migrations that support them. If that works
out, then I can decide what to do about the controllers and views later.&lt;/p&gt;
&lt;p&gt;That said, the lines between what is needed to create a product and
the rest of the system are not always so clear cut. You have to cut
somewhere, though, so I cut like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Image&lt;/li&gt;
&lt;li&gt;InventoryUnit&lt;/li&gt;
&lt;li&gt;OptionType&lt;/li&gt;
&lt;li&gt;OptionValue&lt;/li&gt;
&lt;li&gt;ProductGroup&lt;/li&gt;
&lt;li&gt;ProductOptionType&lt;/li&gt;
&lt;li&gt;ProductProperty&lt;/li&gt;
&lt;li&gt;Product&lt;/li&gt;
&lt;li&gt;ProductScope&lt;/li&gt;
&lt;li&gt;Property&lt;/li&gt;
&lt;li&gt;Prototype&lt;/li&gt;
&lt;li&gt;Variant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;models&#34;&gt;Models&lt;/h3&gt;
&lt;p&gt;Each of these has a model file in spree/core/app/models, which I
just copied over to the app/models directory of my engine.&lt;/p&gt;
&lt;h3 id=&#34;migrations&#34;&gt;Migrations&lt;/h3&gt;
&lt;p&gt;It’d be convenient if I could have just carved the appropriate parts out
of the schema.rb file for the migration. But said file does not
appear to be in evidence. Building a spree instance, and trying to
coerce one out of it just seems too annoying, so I did something else.&lt;/p&gt;
&lt;p&gt;I started from the first migration, removed all of the table
definitions that didn’t interest me and manually applied all of the
migrations in the migration file to the remaining definitions. By
manually applied, I mean I went through each migration file one at a time
and made the specified change to the original set of definitions. There
are, of course, all kinds of reasons why this is a terrible idea. For
a reasonably small set of tables with a simple set of relations, the
trade-off isn’t too bad.&lt;/p&gt;
&lt;h3 id=&#34;migration-generator&#34;&gt;Migration Generator&lt;/h3&gt;
&lt;p&gt;With a single migration in hand, I followed the tutorial at
&lt;a href=&#34;https://web.archive.org/web/20110423220449/http://www.themodestrubyist.com/2010/03/16/rails-3-plugins---part-3---rake-tasks-generators-initializers-oh-my/&#34;&gt;here&lt;/a&gt;
as a guide to create a generator for it in the engine. With
the migration set up as a generator, I went to my sandbox
rails app and ran the migration by doing the following:&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;$ rails g active_product create_migration_file
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ rake db:migrate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;did-it-work&#34;&gt;Did it Work?&lt;/h3&gt;
&lt;p&gt;At this point, I had some the tables in the database and the model files in
place it was time to see if things worked.&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;$ rails console
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rb(main):001:0&amp;gt; p = Product.new&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&amp;hellip;and I got a big wall of error messages. So I could not even
instantiate the class, much less start using it. Well, I kind of
expected that.&lt;/p&gt;
&lt;h3 id=&#34;missing-constants&#34;&gt;Missing Constants&lt;/h3&gt;
&lt;p&gt;Following the error messages started with an unresolved dependency
on &lt;strong&gt;delegate_belongs_to&lt;/strong&gt;. A little sleuthing lead me
into the Spree lib directory were a copy of this plugin lives. Some
further knocking around the interwebs lead me to
&lt;a href=&#34;https://web.archive.org/web/20100621082539/http://github.com:80/faber/delegate_belongs_to&#34;&gt;this project&lt;/a&gt; which appears to be the canonical version of the plugin.
Since I was trying to create a stand alone module, I wanted set this up as an external
dependency (which I will defer figuring out how to enforce in the
engine until later).&lt;/p&gt;
&lt;p&gt;As an aside, (the newer version of) delegate_belongs_to has an
issue with and API change in ActiveRecord for Rails 3. A version that
will at least load with ActiveProduct can be found
&lt;a href=&#34;https://github.com/sonny/delegate_belongs_to&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The active_product engine currently assumes that
delegates_belongs_to is available in the project that it is installed
in. I set it up as a normal plugin in the vendor/plugins directory.&lt;/p&gt;
&lt;h3 id=&#34;circular-dependencies&#34;&gt;Circular Dependencies&lt;/h3&gt;
&lt;p&gt;With that out of the way, the next error seemed to be about Variants
and Scope. In spree/core/lib/scopes there are a couple of files that
interact with the Product and Variant classes in a somewhat messy
way. In order to make use of the scopes that are defined there, I needed to
pull them in. Ultimately, it probably makes sense to include the
changes directly into the affected class files. Since I was
still experimenting here, I just moved them to approximately the
same place in the engine.&lt;/p&gt;
&lt;p&gt;It turns out that the dependency relationships between Product,
Variant, and the Scopes module are pretty complex. I spent a fair
amount of time trying to sort them out manually, but was unable to
find any reasonable way. Eventually, I decided to give up and fall back on
the auto loader to handle it for me.&lt;/p&gt;
&lt;p&gt;The auto-loader in Rails seems to cover a multitude of sins.
A well behaved independent module will need to remove all of these
interdependencies. There are a couple of significant problems with leaving them be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Other potential users of such a module will not necessarily make
sure that everything is auto-loaded, so a this module would just be broken.
Sinatra comes to mind.&lt;/li&gt;
&lt;li&gt;Interdependencies increase complexity. While complexity is not
inherently bad, it can be a source of bugs and errors, so it ought to
be avoided when possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To start with, I moved the scope.rb file and the scope directory
to active_product/lib/auto. And I added the following to the
ActiveProduct module definition in active_product/lib/active_product/engine.rb:&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;ActiveProduct&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;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Engine&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Engine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config.autoload_paths += &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;%W(&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;config.root&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;/lib/auto)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are two interesting things about this to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The engine lib directory is &lt;strong&gt;not&lt;/strong&gt; auto-loaded in
the same way that app/models, app/controllers, etc are. There
apparently is no convention for loading a lib directory. I picked
‘lib/auto’, but there are not any constraints on what can be added.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The engine has its own config variable that is loaded and honored
as part of the Rails app config.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;now-what&#34;&gt;Now What?&lt;/h3&gt;
&lt;p&gt;Now when I tried to instantiate the class I found that it called a
couple of methods that I’m not quite sure what I am going to do with
yet. These are:&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;make_permalink
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;search_methods&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;make_permalink&lt;/strong&gt; is provided by an interestingly
named Railslove module in the spree_core lib and seems harmless
enough. For now, I commented the call out of the Product lib.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;search_methods&lt;/strong&gt; is provided by the
&lt;a href=&#34;https://github.com/activerecord-hackery/meta_search&#34;&gt;MetaSearch&lt;/a&gt;
plugin which is a Spree dependency. Search is neat, but I’ll sort it
out later. Again, I commented it out and will deal with it if it
causes problems.&lt;/p&gt;
&lt;h3 id=&#34;where-are-we-now&#34;&gt;Where are we now&lt;/h3&gt;
&lt;p&gt;I can now instantiate a new product object from the console. That
seems to somewhat validate the effort to isolate the module. You may
be tempted to ask if you could use that product instance for anything;
saving a copy to the data store, for instance. Here’s a hint:
circular dependencies.&lt;/p&gt;
&lt;p&gt;The code that I’ve worked on up till now can be seen &lt;a href=&#34;https://github.com/sonny/active_product/tree/blog_post_1&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>A Ruby on Rails Tag Cloud Tutorial with Spree</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/03/rails-tag-cloud-tutorial-spree/"/>
      <id>https://www.endpointdev.com/blog/2011/03/rails-tag-cloud-tutorial-spree/</id>
      <published>2011-03-07T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;/blog/2011/02/in-our-own-words/&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2011/03/rails-tag-cloud-tutorial-spree/image-0.png&#34; width=&#34;754px&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A tag cloud from a recent End Point blog post.&lt;/p&gt;
&lt;p&gt;Tag clouds have become a fairly popular way to present data on the web. One of our Spree clients recently asked End Point to develop a tag cloud reporting user-submitted search terms in his Spree application. The steps described in this article can be applied to a generic Rails application with a few adjustments.&lt;/p&gt;
&lt;h3 id=&#34;step-1-determine-organization&#34;&gt;Step 1: Determine Organization&lt;/h3&gt;
&lt;p&gt;If you are running this as an extension on Spree pre-Rails 3.0 versions, you’ll create an extension to house the custom code. If you are running this as part of a Rails 3.0 application or Spree Rails 3.0 versions, you’ll want to consider creating a custom gem to house the custom code. In my case, I’m writing a Spree extension for an application running on Spree 0.11, so I create an extension with the command script/generate extension SearchTag.&lt;/p&gt;
&lt;h3 id=&#34;step-2-data-model--migration&#34;&gt;Step 2: Data Model &amp;amp; Migration&lt;/h3&gt;
&lt;p&gt;First, the desired data model for the tag cloud data should be defined. Here’s what mine will look like in this tutorial:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/03/rails-tag-cloud-tutorial-spree/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5581403495920932162&#34; src=&#34;/blog/2011/03/rails-tag-cloud-tutorial-spree/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 167px; height: 113px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next, a model and migration must be created to introduce the class, table and it’s fields. In Spree, I run script/generate extension_model SearchTag SearchRecord and update the migration file to contain the following:&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;CreateSearchRecords&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:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;up&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;:search_records&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;      t.string &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:term&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      t.integer &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:count&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:null&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:#00d;font-weight:bold&#34;&gt;0&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:#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;down&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drop_table &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:search_records&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;I also add a filter method to my model to be used later:&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;SearchRecord&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 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;filter&lt;/span&gt;(term)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    term.gsub(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\+/&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;      .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;#39; &amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .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;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .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;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .downcase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .gsub(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/[^0-9a-z\s-]/&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;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;step-3-populating-the-data&#34;&gt;Step 3: Populating the Data&lt;/h3&gt;
&lt;p&gt;After the migration has been applied, I’ll need to update the code to populate the data. I’m going to add an after filter on every user search. In the case of using Spree, I update search_tag_extension.rb to contain the following:&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;activate&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ProductsController&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchTagCloud&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ProductsController&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;And my custom module contains the following:&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;Spree::SearchTagCloud::ProductsController&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:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;included&lt;/span&gt;(controller)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    controller.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;      controller.append_after_filter &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:record_search&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:only&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:index&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:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;record_search&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; params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:keywords&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      term = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchRecord&lt;/span&gt;.filter(params[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:keywords&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;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; term == &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;      record = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchRecord&lt;/span&gt;.find_or_initialize_by_term(term)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      record.update_attribute(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:count&lt;/span&gt;, record.count+&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;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 module appends an after filter to the products#index action. The after filter method cleans the search term and creates a record or increments the existing record’s count. If this is added directly into an existing Rails application, this bit of functionality may be added directly into one or more existing controller methods to record the search term.&lt;/p&gt;
&lt;h3 id=&#34;step-4-reporting-the-data&#34;&gt;Step 4: Reporting the Data&lt;/h3&gt;
&lt;p&gt;To present the data, I create a controller with script/generate extension_controller SearchTag Admin::SearchTagClouds first. I update config/routes.rb with a new action to reference the new 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;map.namespace &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:admin&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |admin|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  admin.resources &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:search_tag_clouds&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:only&lt;/span&gt; =&amp;gt; [&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:index&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;And I update my controller to calculate the search tag cloud data, shown below. The index method method retrieves all of the search records, sorts, and grabs the the top x results, where x is some configuration defined by the administrator. The method determines the linear solution for scaling the search_record.count to font sizes ranging from 8 pixels to 25 pixels. This order of terms is randomized (&lt;a href=&#34;https://ruby-doc.org/core-2.5.1/Array.html#M000284&#34;&gt;.shuffle&lt;/a&gt;) and linear equation applied. This linear shift can be applied to different types of data. For example, if a tag cloud is to show products with a certain tag, the totals per tag must be calculated and scaled linearly.&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;Admin&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchTagCloudsController&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Admin&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;BaseController&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;index&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    search_records = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchRecord&lt;/span&gt;.all
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .collect { |r| [r.count, r.term] }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .sort
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .reverse[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;..&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchTagCloud&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:count&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    max = search_records.empty? ? &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; : search_records.first.first
&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;# solution is: a*x_factor - y_shift = font size&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;# max font size is 25, min is 8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x_factor = (&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchTagCloud&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:max&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;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchTagCloud&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:min&lt;/span&gt;]) / max.to_f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    y_shift = max.to_f*x_factor - &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Spree&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SearchTagCloud&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Config&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:max&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:#33b&#34;&gt;@results&lt;/span&gt; = search_records.shuffle.inject([]) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |a, b|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      a.push([b[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].to_f*x_factor - y_shift, b[&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;      a
&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 data is presented to the user in the following view:&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;&amp;lt;h3&amp;gt;Tag Cloud:&amp;lt;/h3&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;div id=&amp;#34;tag_cloud&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;% @results.each do |b| %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;span style=&amp;#34;font-size:&amp;lt;%= b[0] %&amp;gt;px;&amp;#34;&amp;gt;&amp;lt;%= b[1] %&amp;gt;&amp;lt;/span&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;% end -%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-5-adding-flexibility&#34;&gt;Step 5: Adding Flexibility&lt;/h3&gt;
&lt;p&gt;In this project, I added configuration variables for the total number of terms displayed, and maximum and minimum font size using Spree’s preference architecture. In a generic Rails application, this may be a nice bit of functionality to include with the preferred configuration architecture.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/03/rails-tag-cloud-tutorial-spree/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5581403499162521858&#34; src=&#34;/blog/2011/03/rails-tag-cloud-tutorial-spree/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 327px; height: 132px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Example tag cloud from the extension.
Additional modifications can be applied to change the
overall styling or color of individual search terms.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;These steps are pretty common for introducing new functionality into an existing application: data migration and model, manipulation on existing controllers, and presentation of results with a new or existing controller and view. Following MVC convention in Rails keeps the code organized and methods simple. In the case of Spree 0.11, this functionality has been packaged into a single extension that is abstracted from the Spree core. The code can be reviewed &lt;a href=&#34;https://github.com/stephskardal/spree-search-tag-cloud&#34;&gt;here&lt;/a&gt;, with a few minor differences.&lt;/p&gt;

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