Ben Morgan I/O

Web Development

Do you like my blog?

I'm a programmer who rants and raves about SEO, social media, web programming, and maybe some physics. If you are in the same field as me, read! I have tutorials.

If you checkout this CHANGELOG diff for the Spree 2.3.0 tag you’ll see that preferences were removed from all the models except for Spree::Calculator, Spree::PromotionRule, and Spree::PaymentMethod. If you’re wanting to store some preferences information, I’ll show you how in this blog post.

For our example, we’re going to assume that we have a store that requires us to collect the first and last name of a user. To do this, we’re going to have to add a new column to our user model.

$ rails g migration add_preferences_to_spree_users

Then, within your migration, you would add the new column and its rollback:

class AddPreferencesToSpreeUsers < ActiveRecord::Migration
  def up
    add_column :spree_users, :preferences, :text
  end

  def down
    remove_column :spree_users, :preferences
  end
end

When I was first doing this, I thought that I would use :hstore instead of :text, but it turns out that they actually don’t store it within PG as JSON, just a textual form of some YAML. I can confirm this when I hit up the rails c and investigate the Spree::Calculator model:

pry(main)> Spree::Calculator
=> Spree::Calculator(id: integer, type: string, calculable_id: integer, calculable_type: string, created_at: datetime, updated_at: datetime, preferences: text)

As we can see, it is a text field that :preferences gets stored as.

Once you have this code, we need to actually set up preferences within the spree user model. Here, I show you how to #class_eval it and add in the preferences:

Spree::User.class_eval do
  preference :first_name, :text
  preference :last_name, :text
end

And that’s it! You’re done!

Now its up to you to be able to setup the views/controllers properly, this blog post was just about setting up preferences for Spree 2.3.

Posted 1 week ago on

I hate JavaScript. I really hate JavaScript. Everyone knows I hate JavaScript. I really hate it. Although I do love Dart. I freaking love Dart! I’d substitute “freak” with another word to go along with the way I usually talk, but considering this blog is public, I’ll be polite. JS sucks!

But lately I’ve been working with AngularJS and having to do some frontend development on not just one, but two spree applications at the same time. At first, I thought it was cool. I could collect the data through the Spree API, but somehow that didn’t seem plausible. the Object can be grabbed from the database via the rails and be passed off to the JavaScript via a rails controller and into an AngularJS controller. Hello headache!

This works actually quite well when you’re on development, but it completely falls apart in production. You have to seperate every single JS file for Angular out from Sprockets and that just not fun. When this occurred, I began using Gon gem. Gon was actually quite efficient in sending over some information, but I couldn’t figure out how to sending over the large amounts of information that Spree comes with even at its bare minimums, pushing me to forfeit this attempt and move onto a new way: inline JS or data hooks. I did inline JS…

Then, on my second Spree project, I gave Gon another try. This time I succeeded and carried my knowledge back over to my original project, further developing my knowledge.

First, lets get you Gon:

# Gemfile
gem 'gon'

Now do a bundle and add this line of code directly before your javascript_include_tag:

<!-- app/views/spree/_head.html.erb -->
<%= include_gon %>

That’s it! You have gon installed!

Now then, lets reopen the products controller and give Gon some data:

# app/controllers/spree/products_controller_decorator.rb
Spree::ProductsController.class_eval do
  alias_method :super_show, :show

  def show
    super_show
    gon.product = @product.gon_object
  end
end

Now, Spree uses a before_action to load up the @product method for the #show page. So we know we have access to @product. The original #show method has also been overwritten, so we’ve just aliased a method called super_show to show and continued with our work.

Once done, when you reload the page you’ll get a method missing for the #gon_object method. Lets fix that by reopening the product model class.

# app/models/spree/products_decorator.rb
Spree::Product.class_eval do
  def gon_object
    {
      id: self.id,
      name: self.name,
      description: self.description,
      available_on: self.available_on,
      deleted_at: self.deleted_at
    }
  end
end

Now if you reload the browser, head on over to the product show page, you’ll see your product, but if you head on over to the console, the gon object will be available to you. (One of the reasons I hate JS is how it defines “hashes/dictionaries” as “objects”. This I find straight up vexing. One small reason I <3 Dart.) Now lets write some JS.

If you type gon.product you’ll notice that you get that product hash right back out. I’m going to let you in on a secret, we didn’t have to write that #gon_object method. We could have just said that gon.product = @product and be done with it. Except this wouldn’t work because this is the exact same hash that you get when you set it equal to @product; there’s too little data.

Try this:

> gon.product.price
undefined

And that is why we have our own custom #gon_object method my friend. You see, the price of an object is stored not in the spree_products table, but in the spree_prices table. You can see an example here of the SQL doing on via the Rails console:

> Spree::Product.first.price
  Spree::Product Load (18.3ms)  SELECT  "spree_products".* FROM "spree_products"  WHERE "spree_products"."deleted_at" IS NULL  ORDER BY "spree_products"."id" ASC LIMIT 1
  Spree::Variant Load (3.4ms)  SELECT  "spree_variants".* FROM "spree_variants"  WHERE "spree_variants"."deleted_at" IS NULL AND "spree_variants"."product_id" = $1 AND "spree_variants"."is_master" = 't' LIMIT 1  [["product_id", 1]]
  Spree::Preference Load (0.6ms)  SELECT  "spree_preferences".* FROM "spree_preferences"  WHERE "spree_preferences"."key" = 'spree/app_configuration/currency' LIMIT 1
  Spree::Price Load (1.1ms)  SELECT  "spree_prices".* FROM "spree_prices"  WHERE "spree_prices"."variant_id" = $1 AND "spree_prices"."currency" = 'USD' LIMIT 1  [["variant_id", 1]]
 => #<BigDecimal:7ff17a31f218,'0.1599E2',18(18)>

Now then, lets edit that hash inside from the #gon_object method inside of Spree::Product. To get the price, we’ll just add a new key called price and give it a pretty simple key:

{
  ...
  price: self.price,
  ...
}

Now, if you go back into the browser console, you’ll notice that it gets written out as a string. (Why is everything saved as a freaking string in JS? Did you know that Dart is optionally typed?) To fixe this, we update the value to self.price.to_f to make it a float number.

Now that you have a way to get custom information from your model to your JS, lets give you a way to get those ActiveRecord relationships working.

Since you’re on the product page, I’m going to assume you need to get at all the different variants for your product. Luckily in spree we can just do:

{
  ...
  variants: self.variants,
  ...
}

That’s awesome! Now we have access to an array of variants! If you have to make variants customized (and I bet you will at some point to get anything useful out of it) then you’ll need to reopen the variants model and add a #gon_object method inside of it as well.

To do so, lets first update our variants value:

{
  ...
  variants: self.variants.map(&:gon_object)
  ...
}

Now, lets reopen the Spree variants model and give it some awesomeness!

Spree::Variant.class_eval do
  def gon_object
    {
      id: self.id,
      sku: self.sku,
      weight: self.weight.to_f,
      height: self.height.to_f,
      width: self.width.to_f,
      depth: self.depth.to_f,
      deleted_at: self.deleted_at,
      is_master: self.is_master,
      cost_price: self.cost_price.to_f,
      position: self.position,
      cost_currency: self.cost_currency,
      track_inventory: self.track_inventory,
      tax_category: self.tax_category
    }
  end
end

This is awesome! Now we know exactly what what data we’re sending to the browser! If you look at the very last piece of data in the hash, you can see that we’re trying to get the tax_category for the variant. This requires use to traverse from our initial variant model, over to the product, and out to the spree_tax_categories table to get our row of data. I’m mentioning this for two reasons. One, its a really good reason for writing your own method for Gon and two, you need to think about n+1 problems. When this happens, please head over to where you originally called the #gon_object and add an #includes method for that table:

# app/models/spree/product_decorator.rb
{
  ...
  variant: self.variants.includes(:variants).map(&:gon_object)
  ...
}

And there we go, you’re all set. You now know how to pass data from Spree to JS large frontend applications. Woot!

Posted 2 weeks ago on

When working with Spree, you’ll need to at some point move some of the work onto a backgorund process. When this happens, you end up having a background smackdown on what to use. There’s delayed jobs, resque, and sidekiq. Not only that, but how optimized is Spree for background jobs?

Well, to start I’ve worked on 3 spree applications in total. Yes, 3. One used delayed jobs, one used resque, and one used sidekiq.

Now, delayed jobs is great if you don’t want to hook redis up. Personelly, I don’t think its the best option for Spree. Spree already goes a bit overkill on ActiveRecord and I feel like delayed jobs might just add more of a weight to it. Not only that, but both resque and sidekiq provide better services via redis, which is a lot faster. In other words, I don’t care if you don’t want to hook redis up to an app, you need to.

When I began working with resque, it was just amazing. Setting up a worker class was also pretty easy and sending all the data over to redis was handled from the app’s end. When something went wrong, I went straight to the dashboard to figure it out. Moving to resque? Do it.

But here’s the thing about resque: its for applications that are high in CPU, but from my experiences, Spree is more memory high than anything else. This is where sidekiq comes in. Not only that, but from working with Rails Dog, I’ve discovered that resque can have a bit of a “split brain”. Due to this, I think it would be best to switch from resque over to sidekiq if one is using resque. The best part: its an easy process.

Posted 3 weeks ago on

Imagine developing an application where you need to grab shipping data in real time, send products, inventory, and order information over at least ten different APIs including Amazon and eBay. When I first heard that I had to do this, there could only be one answer: “Challenge accepted”.

If you’re reading this, then you probably know what Ninefold does and why you would use Spree, but let me introduce you to Wombat: the operating system for ecommerce applications.

Now, Wombat has an enormous amount of documentation on how to use their app, but to be frank: they don’t have a lot yet when it comes to hooking Wombat up to Spree.

In this blog post, we’re going to go from start to finish on building a Wombat application and then deploying it onto Ninefold.

Getting Started with Wombat

Lets first install spree with these commands:

$ rails new wombat_app --database postgresql
$ cd wombat_app
$ spree install -A

Now that your store is set up, lets get Wombat installed. Simply add Wombat to your Gemfile:

gem 'spree_wombat', github: 'spree/spree_wombat', branch: '2-3-stable'

And when you’re ready:

$ bundle
$ bundle exec rails g spree_wombat:install

Once that is finished, head over to Wombat and sign up for a two week trial. When you’re set up, update config/initializers/wombat.rb to include your connection token and ID.

Wombat Configuration

Inside of the Wombat configuration, you’ll see that there’s a #push_objects method. In here, you will have an array of strings that Wombat can use to identify which object you want to send up to Wombat.

Inside of the #payload_builder this is where you’ll add the JSON to get pushed up to Wombat. The :root is used to tell Wombat exactly what type of JSON you’re giving it. You want this because in Wombat, you call your users “customers”, therefore you would need a way to specify how Wombat should interpret the data vs your application (this is how Wombat is made universal and app-agnostic). Now, to grab the JSON that Wombat will need to send up to cloud, you use a serializer. Wombat comes with some serializers upfront (except a user serializer, but I’ll show you how to make that further on).

Finally, we have #push_url which will push the data to wombat. Leave it to its default unless you customize your Wombat profile; you’ll know if you need to change it.

Pushing Data Manually to Wombat

If you set up Spree and Wombat correctly, then you should be able to send two Spree example orders up to Wombat. To do this, lets enter the rails console (with rails c):

> Spree::Wombat::Client.push_batches("Spree::Order")

If you check out your Wombat page, you should see that those orders have been pushed up. If you want to send all of the objects that you have defined in Wombat (and there are times that you will want to do this), then you would use Wombat’s built in rake task:

$ bundle exec rake wombat:push_it

If you check out Wombat, you should see all of your products up there as well.

Adding a Wombat Serializer

This is all fine and dandy, but you happen to have some shipments you want up on Wombat as well. Thankfully, Wombat already has this shipment serializer predone for you! Lets set this up within our application.

Set your #push_objects to include the "Spree::Shipment" model:

config.push_objects = ["Spree::Order", "Spree::Product", "Spree::Shipment"]

Now, inside of your #payload_builder, you’ll want to make sure you add it as a key ("Spree::Shipment") with a Hash as a value ({ serializer: "Spree::Wombat::ShipmentSerializer", root: "shipments" }). This should give you:

config.payload_builder = {
  "Spree::Order" => { serializer: "Spree::Wombat::OrderSerializer", root: "orders" },
  "Spree::Product" => { serializer: "Spree::Wombat::ProductSerializer", root: "products" },
  "Spree::Shipment" => { serializer: "Spree::Wombat::ShipmentSerializer", root: "shipments" }
}

Now, if you perform Spree::Wombat::Client.push_batches("Spree::Shipment") you should be able to see it in Wombat.

Adding a Custom Serializer

You now have three objects hitting Wombat. Wombat lists four objects it will take by defualt. Let’s give it the final one: customers!

To begin, we will generate the serializer using Wombat’s serializer generator:

bundle exec rails g spree_wombat:serializer Spree::User UserSerializer

Within the app/serializers/user_serializer.rb this is the code that I use:

require 'active_model/serializer'

class UserSerializer < ActiveModel::Serializer
  attributes :id, :firstname, :lastname, :email, :shipping_address, :billing_address

  def id
    object.id
  end

  def firstname
    object.try :first_name
  end

  def lastname
    object.try :last_name
  end

  def email
    object.email
  end

  def shipping_address
    address_hash object.shipping_address
  end

  def billing_address
    address_hash object.billing_address
  end

  private

  def address_hash(address)
    {
      address1: address.address1,
      address2: address.address2,
      zipcode: address.zipcode,
      city: address.city,
      state: address.state.name,
      country: address.country.name,
      phone: address.phone
    }
  end
end

Your #push_objects should now look like:

config.push_objects = ["Spree::Order", "Spree::Product", "Spree::Shipment", "Spree::User"]

And your #payload_builder should be:

config.payload_builder = {
  "Spree::Order" => { serializer: "Spree::Wombat::OrderSerializer", root: "orders" },
  "Spree::Product" => { serializer: "Spree::Wombat::ProductSerializer", root: "products" },
  "Spree::Shipment" => { serializer: "Spree::Wombat::ShipmentSerializer", root: "shipments" },
  "Spree::User" => { serializer: "UserSerializer", root: "customers" }
}

Using After Commits with Wombat

Now that this is all done, you’re probably tired of doing it manually. Using decorators, you can add some after_commits to push the model’s data up to Wombat when an ActiveRecord transaction has finished. Here’s an example using the Spree::Shipment model:

# app/models/spree/shipment_decorator.rb
Spree::Shipment.class_eval do
  after_commit :wombat_push_spree_shipments, on: [:create, :update]

  private

  def wombat_push_spree_shipments
    Spree::Wombat::Client.push_batches("Spree::Shipment")
  end
end

Now, if you create a new shipment (and follow this pattern for your other three models) then you should be able to get your data up to wombat in realtime.

Using Workers with Wombat

There’s an issue with this though. What if the page takes two seconds to load and eight seconds to get updated on Wombat? That means that you’ll have eight seconds of blocking code and the amount of time on the server to render the next page for the user would be ten seconds. To get around this, we’re going to set up a Wombat worker and get it done using Resque.

To begin, we’re going to need to install Redis. To do so, follow Redis Rails installation instructions. When finished, add Resque to your Gemfile:

gem 'resque', '~> 1.25', require: 'resque/server'

After bundling, start up these three servers:

web: rails s
redis: redis-server redis.conf
worker: bundle exec rake resque:work QUEUE=* TERM_CHILD=1

You should be good to go. Now, inside fo your Spree::Shipment model, update it to:

Spree::Shipment.class_eval do
  after_commit :wombat_push_spree_shipments, on: [:create, :update]

  private

  def wombat_push_spree_shipments
    Resque.enqueue(WombatWorker, "Spree::Shipment")
  end
end

The only ine that we’ve changed is line 7 where we state that we’re going to be using Resque. The first argument is WombatWorker which is the class of the worker that will handle anything and everything Wombat. First, in your application.rb add the workers directory to your load path:

# Load application's workers
Dir.glob(File.join(File.dirname(__FILE__), "../app/workers/*_worker.rb")) do |c|
  Rails.configuration.cache_classes ? require(c) : load(c)
end

When finished, restart your web server and make a new file called app/workers/wombat_worker.rb:

# app/workers/wombat_worker.rb
class WombatWorker
  @queue = :wombat_worker_queue

  def self.perform(wombat_payload)
    Spree::Wombat::Client.push_batches(wombat_payload)
  end
end

Using @queue we name the queue using a symbol and then use #perform to execute the worker code. Here, wombat_payload will be whatever our second argument is in the Resque.enqueue method that we added within the spree shipment decorator.

Deploying to Ninefold

Lets get this app onto Ninefold. First, we’ll need a Procfile:

# Procfile
redis: redis-server redis.conf
worker: bundle exec rake resque:work QUEUE=* TERM_CHILD=1

Push up all your code to Github and then head over to Ninefold to sign up as a new user.

When done, sign in and create a new application. Select where you’re storing your code (such as Github) and then you should be on step 2. If not, you’ll be asked to sign into Github through Ninefold. Now, you specify the account, repo, and which branch or release that you would like to deploy to. It will then ensure that you are using PostgreSQL, that you do not have rails_12factor gem, and that your application is a valid Rails app.

On step 3, you get to choose your architecture. You’re going to need a web server, so go ahead and make sure you’re using the 2GB - 1vCPU in whichever region is closest to you. Make sure you have a dedicated PG database server for your app (makes it easy to scale). Yes, you would like a background worker for your app and you will want it dedicated. Also make sure that this is a 1GB - 1vCPU server. Finally, push the “Next” button and you should be on Step 4 deployin gyour application

Wrapping Up

Throughout this blog post, you have discovered how to install Spree and Wombat, send data to Wombat, set up serializers, set up a WombatWorker, and finally deploy it onto production using Ninefold. I hope this blog post was informative and useful. Have fun!

Posted 1 month ago on