Homepage

How Spree handles overriding on the frontend and the backend

A couple months ago, after signing a contract to build an unusual e-commerce site, we were forced to make a choice. We’d build the site either from scratch or on top of existing solutions. After a few days of getting our heads around the topic, we decided to take on the latter option. Since we cherish Ruby, we chose the most popular Ruby based e-commerce engine: Spree.

Couple months ago, after signing a contract to build an unusual e-commerce site, we were forced to make a choice. We’d build the site either from scratch or on top of existing solution. After a few days of getting our heads around the topic, we decided to take on the latter option.

Since we cherish Ruby, we chose the most popular Ruby based e-commerce engine: Spree.

We’ve downloaded the code and within minutes the app was up and running. Here is where we’ve spotted first rocks on the road...

Overriding controller actions

Spree provides tools for tweaking controller’s action behaviour. You can change its response using respond_override method. However it affects only methods which use respond_with and yes, not everywhere respond_with is used. Since it is basically designed to change the output of an action, we can do something like:

tsx

It’s fine if that’s all you need. If, however, you’d like to apply some changes to the object/collection, doing it within the method’s body is not the best idea.

Salvation comes with callbacks. Before and after each database interaction (in controllers) you can invoke callbacks to adjust the object you are working with. All you need to do is extend the controller with decorators (more on that later), add create.after :some_method and some_method will be called. It's self explanatory, ain’t it? Strange thing is, there’s no mention about this feature in official Spree docs.

Let me show a quick example that will help you understand what I mean. If for whatever reason, you’d have a has_many :users relation to Product model and would like to initiate that relation instead of copying the whole action you’d have to do something like this:

tsx

This is elegant enough and can be used in various contexts, e.g. before or after creating an object.

Hint: object_name is a function inherited from ResourceController. It returns a string containing current object name, which, in this case, will be “product”.

More on decorators

Decorators can be actually used to adjust any method, no matter if it’s in a model or controller. They are easy to use, all you’ve got to do is:

  1. go to a directory where the file we want to extend is placed, e.g. app/models

  2. create a file matching the following pattern:#{file_name}_decorator.rb

  3. put a piece of code inside:

    Spree::Product.class_eval do # code end

and you are ready to rock this house.

By using this piece of code we make Spree::Product model and all its methods and associations available for tweaking. We can add new methods and override existing ones. Removing is not allowed, but you can always override a method with an empty one, right?

Anyway, overriding whole methods may be easy, adjusting their behaviour is less elegant though. You need to copy method’s body and make the change inside. Luckily Spree’s methods are usually small, but once in a while you may come across a whale-method and there’s not much you can do about it. Just copy, adjust and forget. Real life example? We had to omit one step in the checkout process, which is handled in Spree by state_machine. To achieve this we had to copy the whole configuration and ...comment ONE line!

Adjusting views like a ninja

On the frontend things do look much better. Overriding whole views is not necessary, because Spree was equipped with a great tool called “Deface”. It gives a lot of power to the frontend developer, who can:

  • Change parts of HTML or CSS code in any view

  • Remove HTML or CSS code

  • Wrap divs, spans, classes or other elements into his own divs, spans and whatnots

Check this:

tsx

All you need to do is put this piece of code into any .rb file in app/overrides directory. Spree will load it automagically. You can put the whole overriding code in one file as well. We chose to name the files accordingly to our views e.g. products_show.rb, admin_products_index.rb.

There is another way of handling these overrides, this one will force you to keep the proper directory structure. Let’s give it a try and use Deface::Override example for this. Here’s what you need to do:

  1. First, in your app/overrides directory create the following directories: spree/shared/_header/,

  2. Then create a file named obliterate_the_spree_logo.html.erb.deface in _header folder. A piece of advice: name it whatever you want, but remember that it should not collide with Deface::Override engine. And don’t forget about .deface extension!

  3. Put this markup inside:

    tsx

And that’s it! You’ll get the same result as in the previous example.

Please note the “namespaced” option in the code. This should prevent name conflicts. And one last thing: naming files accordingly to views names will result in automagical view override.

You are not alone in the wilderness

Spree offers the power of quick setup, but don’t get yourself fooled. Before you go down the rabbit hole:

  • Read the code

  • Read the documentation

  • Analyze if extending the platform with your planned modules/features won’t be too much of a problem

Make the “Enter the Wonderland” decision a conscious one.

Of course there’s plenty of gems and add-ons available. As well as content on stackoverflow. Help is around, but if you plan to change Spree’s flow and functionality strongly, I’d suggest creating the app from scratch instead.

Till next time!

Let’s Create a Great Website Together

We'll shape your web platform the way you win it!

More posts in this category

  • February 05, 2025 • 10 min read

    API-first CMS: What Options You Have (Web Dev Agency's Take)

    According to the 2024 State of the API Report by Postman, 74% of organizations are adopting API-first strategies. This statistic isn’t just impressive—it signals a major shift in how businesses operate. While API-first approaches have long been a staple in software development, they're now reshaping content management as well. More and more companies are realizing that traditional CMS platforms can't keep up with the demand for flexibility, speed, and seamless integrations.

    READ MORELearn more about api first cms
  • January 23, 2025 • 15 min read

    Best CMS for SaaS: Top Cloud-Based Solutions

    Choosing the right Content Management System (CMS) is a critical decision for your SaaS business. Your unique needs require solutions that are not only flexible, scalable, and user-friendly but also tailored to meet the demands of a fast-paced, customer-focused industry. A CMS should simplify your workflows and help you deliver personalized, high-quality digital experiences.

    READ MORELearn more about best cms for saas top cloud based solutions
  • December 12, 2024 • 10 min read

    We Picked the Best (Headless) CMS for Vue

    Picture a digital experience where content effortlessly flows across platforms, development is agile, and performance is unmatched. By combining the power of Vue.js, a progressive JavaScript framework, with a modern headless CMS, you can achieve all this and more.

    READ MORELearn more about headless cms for vue