0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

I created a simple gem for Solidus on Ruby on Rails for learning purposes

Posted at

We are currently making an online store with Solidus and Ruby on Rails that sells second hand Japanese goods overseas. As each item is unique and with its own flaws or characteristics, I needed a tool to keep track of them. For this reason, I decided to create a simple gem that allows users to add tags to products via Solidus admin panel. I am a beginner programmer, so I thought that this was a good task to improve my skills and also solve a real problem I had with keeping track of the stock. The gem can be found here: https://github.com/ankuanku1/solidus_product_tags

I started off by writing the command

'bundle gem solidus_product_tags --test=rspec'

in the CLI. This created a scaffold directory and files such as the Gemfile and .gemspec. It also sets\ up RSpec for testing. After this, I added the following to the lib/solidus_product_tags.rb to initialize the gem to use Solidus models and admin backend:

require "solidus_core"
require "solidus_backend"
require "solidus_support"
require "solidus_admin"

require "solidus_product_tags/engine"

For the engine of the gem, I kept it pretty minimal. I used isolate_namespace to create a new namespace for the routes and models to avoid any clashes with our main app.

module SolidusProductTags
  class Engine < Rails::Engine
    engine_name "solidus_product_tags"
    isolate_namespace SolidusProductTags

    initializer "solidus_product_tags.load_product_taggable" do
      config.to_prepare do
        ::Spree::Product.include SolidusProductTags::ProductTaggable
      end
    end
  end
end

Next, I created the models for the gem where the real functionality happens.

Tag: stores the name of each tag. It makes sure each tag has a name and that it is unique. It also deletes the tags when the product is deleted thanks to dependent: :destroy.

class Tag < ActiveRecord::Base
  self.table_name = 'solidus_product_tags_tags'
  has_many :product_taggings, dependent: :destroy
  has_many :products, through: :product_taggings, class_name: "Spree::Product"
  validates :name, presence: true, uniqueness: true
end

ProductTagging: connects a product to a tag via a join table.

class ProductTagging < ActiveRecord::Base
  belongs_to :product, class_name: "Spree::Product"
  belongs_to :tag, class_name: "SolidusProductTags::Tag"
end

ProductTaggable: a module that adds the tagging logic to products. I also created two helper methods for writing and displaying the tags as comma-separated strings.

module SolidusProductTags
  module ProductTaggable
    extend ActiveSupport::Concern

    included do
      has_many :product_taggings,
               class_name: "SolidusProductTags::ProductTagging",
               foreign_key: :product_id,
               dependent: :destroy

      has_many :tags,
               through: :product_taggings,
               class_name: "SolidusProductTags::Tag"
    end

    def tag_list
      tags.map(&:name).join(", ")
    end

    def tag_list=(names)
      self.tags = names.split(",").map do |name|
        SolidusProductTags::Tag.find_or_create_by(name: name.strip.downcase)
      end
    end
  end
end

To actually store the tags inside the app, I needed to make changes to the schema.rb and add tables. One for the tags themselves, and another to link tags to products. I created two migration files inside the gem to handle this. The first migration creates a table for the tags:

class CreateTags < ActiveRecord::Migration[8.0]
  def change
    create_table :solidus_product_tags_tags do |t|
      t.string :name, null: false
      t.timestamps
    end

    add_index :solidus_product_tags_tags, :name, unique: true
  end
end

Next, I created a migration to connect the tags to the products:

class CreateProductTaggings < ActiveRecord::Migration[8.0]
  def change
    create_table :solidus_product_tags_product_taggings do |t|
      t.references :product, null: false, foreign_key: { to_table: :spree_products }
      t.references :tag, null: false, foreign_key: { to_table: :solidus_product_tags_tags }
      t.timestamps
    end
  end
end

The migrations live inside the gem, but can be easily copied using this command after adding the gem to the app's Gemfile:

bin/rails generate solidus_product_tags:install

Now that I had the basics done, I needed to figure out a way to make the form appear on the Solidus admin product creation or editing page. I had previously added a link to the Solidus admin sidebar, so I thought that it would be an easy task. However, I ran into some problems, as is often the case. When adding the sidebar link, I overrode a view from the solidus_admin gem that was responsible for rendering the sidebar, and simply added the link there. This time as well, I tried the same approach. I found a file that I thought was rendering the product creation page, copied it to my gem, and added the tagging form there. No matter what I tried I just couldn't get it to appear on the page. I then opened the browser's developer tools to see what the code was (something I should have done right away and will from now on) and found out that the view actually comes from the solidus_backend gem! Once I knew this, I simply found the responsible file in the solidus_backend gem and copied it to the gem, adding a _form.html.erb file:

   <div data-hook="admin_product_form_tags">
            <%= f.field_container :tag_list do %>
              <%= f.label :tag_list, "Tags (comma separated)" %>
              <%= f.text_field :tag_list, value: @product.tag_list, class: 'fullwidth' %>
            <% end %>
          </div>

To finish up, I wrote a simple README.md, CHANGELOG.md and some tests to see that the code was doing what I expected it to do. After requesting a review from a team member, I cleaned up the code base by removing some unneeded files that I used to try to access the view through solidus_admin gem, wrote more tests on edge cases and added more information to the README.md.

Final thoughts, I learned a lot about how Solidus backend works and how gems are created. More features could be added later to this gem such as frontend visibility or a dedicated page on the admin side where the tagged products are listed and sorted.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?