diff --git a/alchemy-solidus.gemspec b/alchemy-solidus.gemspec
index fd16b74..8c16fa2 100644
--- a/alchemy-solidus.gemspec
+++ b/alchemy-solidus.gemspec
@@ -20,6 +20,7 @@ Gem::Specification.new do |gem|
gem.add_dependency('deface', ['~> 1.0'])
gem.add_development_dependency('rspec-rails', ['~> 3.7'])
+ gem.add_development_dependency('shoulda-matchers', ['~> 4.0'])
gem.add_development_dependency('capybara', ['~> 2.15'])
gem.add_development_dependency('capybara-screenshot', ['~> 1.0'])
gem.add_development_dependency('factory_bot', ['~> 4.8'])
diff --git a/app/models/alchemy/essence_spree_variant.rb b/app/models/alchemy/essence_spree_variant.rb
new file mode 100644
index 0000000..44c3d6f
--- /dev/null
+++ b/app/models/alchemy/essence_spree_variant.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Alchemy
+ class EssenceSpreeVariant < ActiveRecord::Base
+ VARIANT_ID = /\A\d+\z/
+
+ belongs_to :variant, class_name: 'Spree::Variant', optional: true
+
+ acts_as_essence(
+ ingredient_column: :variant,
+ preview_text_method: :name
+ )
+
+ def ingredient=(variant)
+ case variant
+ when VARIANT_ID
+ self.variant = Spree::Variant.new(id: variant)
+ when Spree::Variant
+ self.variant = variant
+ else
+ super
+ end
+ end
+ end
+end
diff --git a/app/views/alchemy/essences/_essence_spree_variant_editor.html.erb b/app/views/alchemy/essences/_essence_spree_variant_editor.html.erb
new file mode 100644
index 0000000..cda6dc0
--- /dev/null
+++ b/app/views/alchemy/essences/_essence_spree_variant_editor.html.erb
@@ -0,0 +1,9 @@
+
+ <%= content_label(content) %>
+ <%= select_tag(
+ content.form_field_name,
+ options_from_collection_for_select(Spree::Variant.all, :id, :name, content.essence.variant_id),
+ id: content.form_field_id,
+ class: 'alchemy_selectbox full_width'
+ ) %>
+
diff --git a/app/views/alchemy/essences/_essence_spree_variant_view.html.erb b/app/views/alchemy/essences/_essence_spree_variant_view.html.erb
new file mode 100644
index 0000000..e69de29
diff --git a/bin/rails b/bin/rails
index 1981c1d..9050151 100755
--- a/bin/rails
+++ b/bin/rails
@@ -9,6 +9,6 @@ APP_PATH = File.expand_path('../../spec/dummy/config/application', __FILE__)
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
-
require 'rails/all'
+require 'rspec-rails'
require 'rails/engine/commands'
diff --git a/db/migrate/20191107135822_create_alchemy_essence_spree_variants.rb b/db/migrate/20191107135822_create_alchemy_essence_spree_variants.rb
new file mode 100644
index 0000000..afab341
--- /dev/null
+++ b/db/migrate/20191107135822_create_alchemy_essence_spree_variants.rb
@@ -0,0 +1,9 @@
+class CreateAlchemyEssenceSpreeVariants < ActiveRecord::Migration[5.2]
+ def change
+ create_table :alchemy_essence_spree_variants do |t|
+ t.references :variant, null: true, foreign_key: { to_table: Spree::Variant.table_name }
+
+ t.timestamps
+ end
+ end
+end
diff --git a/spec/models/alchemy/essence_spree_variant_spec.rb b/spec/models/alchemy/essence_spree_variant_spec.rb
new file mode 100644
index 0000000..6fc05d7
--- /dev/null
+++ b/spec/models/alchemy/essence_spree_variant_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'alchemy/test_support/essence_shared_examples'
+require 'alchemy/test_support/factories/page_factory'
+require 'alchemy/test_support/factories/element_factory'
+require 'alchemy/test_support/factories/content_factory'
+require 'spree/testing_support/factories/variant_factory'
+
+require_dependency 'alchemy/site'
+
+RSpec.describe Alchemy::EssenceSpreeVariant, type: :model do
+ let(:variant) { build(:variant) }
+ let(:essence) { described_class.new(variant: variant) }
+
+ it_behaves_like 'an essence' do
+ let(:ingredient_value) { variant }
+ end
+
+ describe 'ingredient=' do
+ subject(:ingredient) { essence.variant }
+
+ context 'when String value is only a number' do
+ let(:value) { '101' }
+
+ before do
+ essence.ingredient = value
+ end
+
+ it 'sets variant to an variant instance with that id' do
+ is_expected.to be_a(Spree::Variant)
+ expect(ingredient.id).to eq(101)
+ end
+ end
+
+ context 'when value is an Spree Variant' do
+ let(:value) { variant }
+
+ before do
+ essence.ingredient = value
+ end
+
+ it 'sets variant to an variant instance with that id' do
+ is_expected.to be_a(Spree::Variant)
+ expect(ingredient).to eq(variant)
+ end
+ end
+
+ context 'when value is not only a number' do
+ let(:value) { 'variant1' }
+
+ it do
+ expect {
+ essence.ingredient = value
+ }.to raise_error(ActiveRecord::AssociationTypeMismatch)
+ end
+ end
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 45342f9..c6125e0 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -31,6 +31,14 @@
# If you are not using ActiveRecord, you can remove this line.
# ActiveRecord::Migration.maintain_test_schema!
+require 'shoulda-matchers'
+Shoulda::Matchers.configure do |config|
+ config.integrate do |with|
+ with.test_framework :rspec
+ with.library :active_record
+ end
+end
+
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
@@ -59,4 +67,6 @@
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
+
+ config.include FactoryBot::Syntax::Methods
end
diff --git a/spec/views/alchemy/essences/essence_spree_variant_editor_spec.rb b/spec/views/alchemy/essences/essence_spree_variant_editor_spec.rb
new file mode 100644
index 0000000..976ab1f
--- /dev/null
+++ b/spec/views/alchemy/essences/essence_spree_variant_editor_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'alchemy/essences/_essence_spree_variant_editor' do
+ let(:content) { Alchemy::Content.new(essence: essence) }
+ let(:essence) { Alchemy::EssenceSpreeVariant.new }
+
+ before do
+ view.class.send(:include, Alchemy::Admin::EssencesHelper)
+ allow(view).to receive(:content_label).and_return(content.name)
+ end
+
+ it "renders a variant select box" do
+ render 'alchemy/essences/essence_spree_variant_editor', content: content
+ expect(rendered).to have_css('select.alchemy_selectbox.full_width')
+ end
+
+ context 'with a variant related to essence' do
+ let(:product) { Spree::Product.new(name: 'Chocolate') }
+ let(:variant) { Spree::Variant.new(id: 1, product: product) }
+ let(:essence) { Alchemy::EssenceSpreeVariant.new(variant_id: variant.id) }
+
+ before do
+ expect(Spree::Variant).to receive(:all) { [variant] }
+ end
+
+ it "selects variant in select box" do
+ render 'alchemy/essences/essence_spree_variant_editor', content: content
+ expect(rendered).to have_css('option[value="1"][selected]')
+ end
+ end
+end