Do not index
Do not index
URL
Homepage
Where we ended last time we still show our default Rails application index page like this:
Let’s change that with our own homepage where we will be showing our products in a little bit.
Generating a
HomeController
with only the index
action:bin/rails g controller home index
and set this new action as our root url by changing the
config/routes.rb
file like so:Rails.application.routes.draw do
# This line mounts Solidus's routes at the root of your application.
# This means, any requests to URLs such as /products, will go to Spree::ProductsController.
# If you would like to change where this engine is mounted, simply change the :at option to something different.
#
# We ask that you don't use the :as option here, as Solidus relies on it being the default of "spree"
mount Spree::Core::Engine, at: '/'
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
root "home#index"
end
When we restart our server and open up http://localhost:3000/ again we get something like this:
Solidus testing support - factory_bot
The testing solidus guide shows you how to use the testing_support factories that are available in solidus_core:
We will add
factory_bot_rails
to our Gemfile
and update application.rb
to load all the factories if factory_bot
is available. We need to add factory_bot_rails to our test and/or development group. I picked them both so I can generate some data from the factories if needed for seed data. group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri mingw x64_mingw ]
gem "factory_bot_rails", "~> 6.2"
end
The snippet below comes directly from the testing guide linked above, adding that to our
config/application.rb
like sorequire_relative "boot"
require "rails"
...
...
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Blogstore
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
# Add support for Solidus testsupport factories
if defined?(FactoryBotRails)
initializer after: "factory_bot.set_factory_paths" do
require "spree/testing_support/factory_bot"
# The paths for Solidus' core factories.
solidus_paths = Spree::TestingSupport::FactoryBot.definition_file_paths
# Optional: Any factories you want to require from extensions.
extension_paths = [
# MySolidusExtension::Engine.root.join("lib/my_solidus_extension/testing_support/factories"),
# or individually:
# MySolidusExtension::Engine.root.join("lib/my_solidus_extension/testing_support/factories/resource.rb"),
]
# Your application's own factories.
app_paths = [
Rails.root.join("test/factories")
]
FactoryBot.definition_file_paths = solidus_paths + extension_paths + app_paths
end
end
end
end
Homepage default store name
Let’s update our generated index page to show the store name. You can have multiple stores in one Solidus installation, but for this series we will start with only the default store available to us. We can use
Spree::Store.default
to get the current default store instance. Showing the default store name on our homepage like so:
<h1><%= Spree::Store.default.name %></h1>
<p>Find me in app/views/home/index.html.erb</p>
And here we go, we now render our store name: “Sample Store”.
To test this we can make a system spec, this also verifies that we can use the factories from Solidus as well. I started with the system spec for the homepage already (and fixed the generated tests for using the root_url instead of the generated route).
test/system/homepage_test.rb
require "application_system_test_case"
class HomepageTest < ApplicationSystemTestCase
setup do
@store = create(:store, name: "BlogStore")
end
test "homepage" do
visit root_url
assert_selector "h1", text: "BlogStore"
end
end
when we run the above spec we are all green!
#> bin/rails test test/system/homepage_test.rb
Running 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 58329
# Running:
Capybara starting Puma...
* Version 5.6.7 , codename: Birdie's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:58294
.
Finished in 9.190615s, 0.1088 runs/s, 0.1088 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
Adding some style
Now that we have a working test that actually grabs our Solidus store name we should add some style with tailwind.
app/views/home/index.html.erb
<section class="grid min-h-screen place-items-center bg-sky-100">
<div class="p-6">
<h1 class="text-center text-4xl font-extrabold leading-tight text-gray-800"><%= Spree::Store.default.name%></h1>
<p class="mt-6 text-center text-lg font-light text-gray-700">Whether you prefer fiction or non-fiction, we have something for everyone. Start browsing now and find your next adventure!</p>
</div>
</section>
Looks much better already! We do have a problem though. When we are running the tests we are getting a very cryptic error like this:
rails test test/system/homepage_test.rb:8
E
Error:
HomeControllerTest#test_should_get_index:
ActionView::Template::Error: Error: Function rgb is missing argument $green.
on line 1 of stdin
>> }.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity
------------------------------------------^
app/views/layouts/application.html.erb:9
test/controllers/home_controller_test.rb:5:in `block in <class:HomeControllerTest>'
rails test test/controllers/home_controller_test.rb:4
We are using
esbuild
with jsbundling
and cssbundling
, this is conflicting with sass
that is added to our system by Solidus. Luckily we can fix that by setting css_compressor
to nil
in our application.rb
# Disable the build in css_compressor.
config.assets.css_compressor = nil
When we run the specs again, they are all passing!
Showing products
Solidus comes with a sample engine installed by default. You can generate sample data by running the following command:
#> bundle exec rake spree_sample:load
sample Payment Methods
sample Tax Categories
sample Tax Rates
sample Shipping Categories
sample Shipping Methods
sample Products
sample Taxons
sample Taxonomies
sample Option Values
sample Option Types
sample Product Option Types
sample Product Properties
sample Variants
sample Stock
sample Assets
sample images for Solidus Snapback Cap
sample images for Solidus Hoodie Zip
sample images for Ruby Hoodie
sample images for Ruby Hoodie Zip
sample images for Ruby Polo
sample images for Solidus Mug
sample images for Ruby Mug
sample images for Solidus Tote
sample images for Ruby Tote
sample Orders
sample Addresses
sample Stores
sample Payments
sample Reimbursements
When you enter the admin panel, a variety of products are automatically generated for you to test with.
We will initially use these to design the homepage and gradually add more logic and tests step-by-step.
To have a starting base, let's show some products on the homepage. Since we are using ActiveStorage, we need to ensure that
ActiveStorage::SetCurrent
is included so that the disk-based storage gets the correct data. Let's add this line to our ApplicationController
.class ApplicationController < ActionController::Base
include ActiveStorage::SetCurrent
end
Solidus also offers a base controller class that includes this and more. We will explore that base controller later in more detail.
For now we will just take the first 4 products and render them in a grid on the homepage:
<section class="grid min-h-screen place-items-center bg-sky-100">
....
....
<div class="grid grid-cols-1 gap-8 p-8 md:grid-cols-2">
<% Spree::Product.take(4).each do |product| %>
<div class="m-12 flex items-center gap-6 rounded-md bg-gray-100 p-6">
<div class="basis-1/3">
<%= image_tag product.gallery.images.first.url(:product), class: "rounded-md" %>
</div>
<div class="basis-2/3">
<h2 class="text-xl font-bold"><%= product.name %></h2>
<p class="mt-1 text-sm leading-relaxed"><%= product.description %></p>
</div>
</div>
<% end %>
</div>
</section>
Refreshing the homepage will show you something like this:
The next steps will be to display the product details and then add the product to our cart.
The source code for this part can be found here: