├── .rspec ├── lib ├── spree_asset_variant_options │ ├── factories.rb │ └── engine.rb ├── solidus_asset_variant_options │ ├── testing_support │ │ └── factories.rb │ ├── version.rb │ └── engine.rb ├── spree_asset_variant_options.rb ├── solidus_asset_variant_options.rb └── generators │ └── solidus_asset_variant_options │ └── install │ └── install_generator.rb ├── config ├── routes.rb └── locales │ └── en.yml ├── spec ├── support │ └── images │ │ └── green.jpeg ├── models │ └── spree │ │ ├── product_decorator_spec.rb │ │ ├── image_decorator_spec.rb │ │ └── variant_decorator_spec.rb └── spec_helper.rb ├── .github ├── dependabot.yml └── stale.yml ├── .gem_release.yml ├── bin ├── setup ├── rails └── console ├── Rakefile ├── .gitignore ├── db └── migrate │ ├── 20140411185638_add_position_to_spree_assets_variants.rb │ ├── 20140115160024_add_id_to_spree_assets_variants.rb │ └── 20131212185245_create_spree_assets_variants.rb ├── .rubocop.yml ├── app ├── overrides │ └── spree │ │ ├── admin │ │ └── images │ │ │ ├── _image_row │ │ │ └── replace_variant_select_with_multi_select.html.erb.deface │ │ │ └── _form │ │ │ └── replace_variant_select_with_multi_select.html.erb.deface │ │ └── products │ │ └── _thumbnails │ │ └── replace_thumbnail_classes_with_classes_including_variant_ids.html.erb.deface ├── decorators │ ├── models │ │ └── spree │ │ │ ├── gallery │ │ │ └── product_gallery_decorator.rb │ │ │ ├── product_decorator.rb │ │ │ ├── image_decorator.rb │ │ │ └── variant_decorator.rb │ └── controllers │ │ └── spree │ │ ├── api │ │ └── images_controller_decorator.rb │ │ └── admin │ │ └── images_controller_decorator.rb └── models │ └── spree │ └── variant_image.rb ├── .circleci └── config.yml ├── Gemfile ├── solidus_asset_variant_options.gemspec ├── LICENSE └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --color -------------------------------------------------------------------------------- /lib/spree_asset_variant_options/factories.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | end 5 | -------------------------------------------------------------------------------- /lib/solidus_asset_variant_options/testing_support/factories.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | end 5 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Spree::Core::Engine.routes.draw do 4 | # Add your extension routes here 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/images/green.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solidusio-contrib/solidus_asset_variant_options/HEAD/spec/support/images/green.jpeg -------------------------------------------------------------------------------- /lib/solidus_asset_variant_options/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SolidusAssetVariantOptions 4 | VERSION = '1.0.2' 5 | end 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /lib/spree_asset_variant_options.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'solidus_core' 4 | require 'solidus_support' 5 | require 'spree_asset_variant_options/engine' 6 | -------------------------------------------------------------------------------- /.gem_release.yml: -------------------------------------------------------------------------------- 1 | bump: 2 | recurse: false 3 | file: 'lib/solidus_asset_variant_options/version.rb' 4 | message: Bump SolidusAssetVariantOptions to %{version} 5 | branch: true 6 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | gem install bundler --conservative 7 | bundle update 8 | bundle exec rake extension:test_app 9 | -------------------------------------------------------------------------------- /lib/solidus_asset_variant_options.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'solidus_core' 4 | require 'solidus_support' 5 | 6 | require 'solidus_asset_variant_options/engine' 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'solidus_dev_support/rake_tasks' 4 | SolidusDevSupport::RakeTasks.install 5 | 6 | task default: %w[extension:test_app extension:specs] 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | \#* 3 | *~ 4 | .#* 5 | .DS_Store 6 | .idea 7 | .project 8 | .sass-cache 9 | coverage 10 | Gemfile.lock 11 | tmp 12 | nbproject 13 | pkg 14 | *.swp 15 | spec/dummy 16 | spec/examples.txt 17 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | options: "Options" 6 | -------------------------------------------------------------------------------- /db/migrate/20140411185638_add_position_to_spree_assets_variants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddPositionToSpreeAssetsVariants < SolidusSupport::Migration[4.2] 4 | def change 5 | add_column :spree_assets_variants, :position, :integer 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - solidus_dev_support/rubocop 3 | 4 | inherit_gem: 5 | solidus_dev_support: .rubocop.yml 6 | 7 | AllCops: 8 | Exclude: 9 | - spec/dummy/**/* 10 | - vendor/**/* 11 | 12 | Rails/SkipsModelValidations: 13 | Exclude: 14 | - db/migrate/**/* 15 | -------------------------------------------------------------------------------- /app/overrides/spree/admin/images/_image_row/replace_variant_select_with_multi_select.html.erb.deface: -------------------------------------------------------------------------------- 1 | 2 | <%= f.select :viewable_ids, options_for_select(@variants, image.variant_ids), {}, { multiple: true, class: 'select2 fullwidth' } %> 3 | -------------------------------------------------------------------------------- /app/overrides/spree/admin/images/_form/replace_variant_select_with_multi_select.html.erb.deface: -------------------------------------------------------------------------------- 1 | 2 | <%= f.select :viewable_ids, options_for_select(@variants, @image.try(:variant_ids)), {}, {multiple: true, :class => 'select2 fullwidth'} %> 3 | -------------------------------------------------------------------------------- /db/migrate/20140115160024_add_id_to_spree_assets_variants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddIdToSpreeAssetsVariants < SolidusSupport::Migration[4.2] 4 | def change 5 | add_column :spree_assets_variants, :id, :primary_key 6 | rename_column :spree_assets_variants, :asset_id, :image_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # frozen_string_literal: true 4 | 5 | app_root = 'spec/dummy' 6 | 7 | unless File.exist? "#{app_root}/bin/rails" 8 | system "bin/rake", app_root or begin # rubocop:disable Style/AndOr 9 | warn "Automatic creation of the dummy app failed" 10 | exit 1 11 | end 12 | end 13 | 14 | Dir.chdir app_root 15 | exec 'bin/rails', *ARGV 16 | -------------------------------------------------------------------------------- /lib/solidus_asset_variant_options/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spree/core' 4 | 5 | module SolidusAssetVariantOptions 6 | class Engine < Rails::Engine 7 | include SolidusSupport::EngineExtensions 8 | 9 | isolate_namespace Spree 10 | 11 | engine_name 'solidus_asset_variant_options' 12 | 13 | # use rspec for tests 14 | config.generators do |g| 15 | g.test_framework :rspec 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20131212185245_create_spree_assets_variants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateSpreeAssetsVariants < SolidusSupport::Migration[4.2] 4 | def change 5 | # rubocop:disable Rails/CreateTableWithTimestamps 6 | create_table :spree_assets_variants, id: false do |t| 7 | t.references :asset, index: true 8 | t.references :variant, index: true 9 | end 10 | # rubocop:enable Rails/CreateTableWithTimestamps 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/decorators/models/spree/gallery/product_gallery_decorator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Spree 4 | module Gallery 5 | module ProductGalleryDecorator 6 | attr_accessor :viewable_ids 7 | 8 | def self.prepended(base) 9 | def images 10 | @images ||= @product.variant_images.collect { |vi| vi.image }.uniq 11 | end 12 | end 13 | 14 | ::Spree::Gallery::ProductGallery.prepend self 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/decorators/models/spree/product_decorator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Spree 4 | module ProductDecorator 5 | def self.prepended(base) 6 | base.has_many :nonuniq_variant_images, 7 | -> { order(:position) }, 8 | source: :variant_images, 9 | through: :variants_including_master 10 | end 11 | 12 | def variant_images 13 | nonuniq_variant_images.reorder(:position).distinct 14 | end 15 | 16 | ::Spree::Product.prepend self 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # frozen_string_literal: true 4 | 5 | require "bundler/setup" 6 | require "solidus_asset_variant_options" 7 | 8 | # You can add fixtures and/or initialization code here to make experimenting 9 | # with your gem easier. You can also use a different console, if you like. 10 | $LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"]) 11 | 12 | # (If you use this, don't forget to add pry to your Gemfile!) 13 | # require "pry" 14 | # Pry.start 15 | 16 | require "irb" 17 | IRB.start(__FILE__) 18 | -------------------------------------------------------------------------------- /app/decorators/models/spree/image_decorator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Spree 4 | module ImageDecorator 5 | attr_accessor :viewable_ids 6 | 7 | def self.prepended(base) 8 | base.has_many :variant_images, class_name: '::Spree::VariantImage', dependent: :destroy 9 | base.has_many :variants, through: :variant_images 10 | end 11 | 12 | def variant_html_classes 13 | variant_ids.map { |variant| "tmb-#{variant}" }.join(" ") 14 | end 15 | 16 | ::Spree::Image.prepend self 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/decorators/models/spree/variant_decorator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Spree 4 | module VariantDecorator 5 | def self.prepended(base) 6 | base.has_many :variant_images, -> { order(:position) }, class_name: '::Spree::VariantImage' 7 | base.has_many :variant_image_images, through: :variant_images, source: :image 8 | 9 | base.alias_method :images, :variant_image_images 10 | base.alias_method :images=, :variant_image_images= 11 | end 12 | 13 | ::Spree::Variant.prepend self 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/spree/variant_image.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Spree 4 | class VariantImage < ApplicationRecord 5 | self.table_name = :spree_assets_variants 6 | belongs_to :image, class_name: 'Spree::Image' 7 | belongs_to :variant, class_name: 'Spree::Variant', touch: true 8 | 9 | acts_as_list 10 | scope :with_position, -> { where("position IS NOT NULL") } 11 | default_scope -> { order("#{table_name}.position") } 12 | 13 | # on create only just in case there are some lingering in the system 14 | validates :image_id, uniqueness: { scope: :variant_id, on: :create } 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/spree_asset_variant_options/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SpreeAssetVariantOptions 4 | class Engine < Rails::Engine 5 | require "spree/core" 6 | isolate_namespace Spree 7 | engine_name "solidus_asset_variant_options" 8 | 9 | # use rspec for tests 10 | config.generators do |g| 11 | g.test_framework :rspec 12 | end 13 | 14 | def self.activate 15 | Dir.glob(File.join(File.dirname(__FILE__), "../../app/decorators/**/*.rb")) do |c| 16 | require_dependency(c) 17 | end 18 | end 19 | 20 | config.to_prepare(&method(:activate).to_proc) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/models/spree/product_decorator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Spree::Product do 6 | let(:product) { create :product } 7 | let(:variant) { create :base_variant, product: product } 8 | let(:image_green) { create :image, viewable_type: 'Spree::Variant' } 9 | let(:image_blue) { create :image, viewable_type: 'Spree::Variant' } 10 | 11 | before do 12 | variant.images << image_green 13 | variant.images << image_blue 14 | variant.save 15 | end 16 | 17 | describe '#variant_images' do 18 | it 'returns unique list of variant images' do 19 | expect(product.reload.variant_images.size).to eq(2) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/models/spree/image_decorator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Spree::Image do 6 | let(:image) { create :image } 7 | let(:variant_small) { create :base_variant } 8 | let(:variant_big) { create :base_variant } 9 | 10 | before do 11 | image.variant_ids = [variant_small.id, variant_big.id] 12 | end 13 | 14 | describe '#variant_html_classes' do 15 | it 'returns list of classes for variants' do 16 | expect(image.variant_html_classes).to eq("tmb-#{variant_small.id} tmb-#{variant_big.id}") 17 | end 18 | end 19 | 20 | describe 'variant relationship' do 21 | it "has many variants" do 22 | expect(image.variants.size).to eq(2) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /lib/generators/solidus_asset_variant_options/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SolidusAssetVariantOptions 4 | module Generators 5 | class InstallGenerator < Rails::Generators::Base 6 | class_option :auto_run_migrations, type: :boolean, default: false 7 | 8 | def add_migrations 9 | run 'bundle exec rake railties:install:migrations FROM=solidus_asset_variant_options' 10 | end 11 | 12 | def run_migrations 13 | run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) 14 | if run_migrations 15 | run 'bundle exec rake db:migrate' 16 | else 17 | puts 'Skipping rake db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/decorators/controllers/spree/api/images_controller_decorator.rb: -------------------------------------------------------------------------------- 1 | module Spree 2 | module Api 3 | module ImagesControllerDecorator 4 | 5 | def update 6 | @image = Spree::Image.accessible_by(current_ability, :update).find(params[:id]) 7 | if params[:image][:viewable_ids].present? && 8 | params[:image][:viewable_ids].reject(&:blank?).present? 9 | @image.variant_ids = params[:image][:viewable_ids].reject(&:blank?) 10 | else #use master variant as falback when empty 11 | @image.variant_ids = [@product.master.id] 12 | end 13 | # reset normal viewable 14 | @image.viewable_type = 'Spree::Variant' 15 | @image.viewable_id = @image.variant_ids.first 16 | 17 | @image.update(image_params) 18 | respond_with(@image, default_template: :show) 19 | end 20 | 21 | ::Spree::Api::ImagesController.prepend self 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/overrides/spree/products/_thumbnails/replace_thumbnail_classes_with_classes_including_variant_ids.html.erb.deface: -------------------------------------------------------------------------------- 1 | 2 | <%# no need for thumbnails unless there is more than one image %> 3 | <% if @product.gallery.images.size > 1 %> 4 | 20 | <% end %> 21 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Configure Rails Environment 4 | ENV['RAILS_ENV'] = 'test' 5 | 6 | # Run Coverage report 7 | require 'solidus_dev_support/rspec/coverage' 8 | 9 | require File.expand_path('dummy/config/environment.rb', __dir__) 10 | 11 | # Requires factories and other useful helpers defined in spree_core. 12 | require 'solidus_dev_support/rspec/feature_helper' 13 | 14 | # Requires supporting ruby files with custom matchers and macros, etc, 15 | # in spec/support/ and its subdirectories. 16 | Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f } 17 | 18 | # Will load Solidus core factory first and then the ones 19 | # defined in `lib/solidus_asset_variant_options/testing_support/factories.rb`. 20 | # and `lib/solidus_asset_variant_options/testing_support/factories`. 21 | SolidusDevSupport::TestingSupport::Factories.load_for(SolidusAssetVariantOptions::Engine) 22 | 23 | RSpec.configure do |config| 24 | config.infer_spec_type_from_file_location! 25 | config.use_transactional_fixtures = false 26 | end 27 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | # Always take the latest version of the orb, this allows us to 5 | # run specs against Solidus supported versions only without the need 6 | # to change this configuration every time a Solidus version is released 7 | # or goes EOL. 8 | solidusio_extensions: solidusio/extensions@volatile 9 | 10 | jobs: 11 | run-specs-with-postgres: 12 | executor: solidusio_extensions/postgres 13 | steps: 14 | - solidusio_extensions/run-tests 15 | run-specs-with-mysql: 16 | executor: solidusio_extensions/mysql 17 | steps: 18 | - solidusio_extensions/run-tests 19 | 20 | workflows: 21 | "Run specs on supported Solidus versions": 22 | jobs: 23 | - run-specs-with-postgres 24 | - run-specs-with-mysql 25 | "Weekly run specs against master": 26 | triggers: 27 | - schedule: 28 | cron: "0 0 * * 4" # every Thursday 29 | filters: 30 | branches: 31 | only: 32 | - master 33 | jobs: 34 | - run-specs-with-postgres 35 | - run-specs-with-mysql 36 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | branch = ENV.fetch('SOLIDUS_BRANCH', 'master') 7 | solidus_git, solidus_frontend_git = if (branch == 'master') || (branch >= 'v3.2') 8 | %w[solidusio/solidus solidusio/solidus_frontend] 9 | else 10 | %w[solidusio/solidus] * 2 11 | end 12 | gem 'solidus', github: solidus_git, branch: branch 13 | gem 'solidus_frontend', github: solidus_frontend_git, branch: branch 14 | 15 | # Needed to help Bundler figure out how to resolve dependencies, 16 | # otherwise it takes forever to resolve them. 17 | # See https://github.com/bundler/bundler/issues/6677 18 | gem 'rails', '>0.a' 19 | 20 | # Provides basic authentication functionality for testing parts of your engine 21 | gem 'solidus_auth_devise' 22 | 23 | case ENV['DB'] 24 | when 'mysql' 25 | gem 'mysql2' 26 | when 'postgresql' 27 | gem 'pg' 28 | else 29 | gem 'sqlite3' 30 | end 31 | 32 | gemspec 33 | -------------------------------------------------------------------------------- /spec/models/spree/variant_decorator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Spree::Variant do 6 | let(:variant) { create :base_variant } 7 | let(:image_green) { create :image } 8 | let(:image_blue) { create :image } 9 | 10 | before do 11 | variant.images << image_green 12 | variant.images << image_blue 13 | end 14 | 15 | describe 'variant relationship' do 16 | it "has many images" do 17 | expect(variant.images.size).to eq(2) 18 | end 19 | end 20 | 21 | describe 'images are ordered by variant image positions' do 22 | # rubocop:disable RSpec/MultipleExpectations 23 | it "returns the images ordered by variant image position" do 24 | variant.variant_images.where(image: image_blue).first.move_higher 25 | variant.images.reload 26 | 27 | expect(variant.images).to eq([image_blue, image_green]) 28 | 29 | variant.variant_images.where(image: image_green).first.move_higher 30 | variant.images.reload 31 | 32 | expect(variant.images).to eq([image_green, image_blue]) 33 | end 34 | # rubocop:enable RSpec/MultipleExpectations 35 | end 36 | 37 | it "cannot associate itself to the same image twice" do 38 | expect { variant.images << image_green }.to raise_error( 39 | ActiveRecord::RecordInvalid, 40 | /Image has already been taken/ 41 | ) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /solidus_asset_variant_options.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $:.push File.expand_path('lib', __dir__) 4 | require 'solidus_asset_variant_options/version' 5 | 6 | Gem::Specification.new do |s| 7 | s.name = 'solidus_asset_variant_options' 8 | s.version = SolidusAssetVariantOptions::VERSION 9 | s.summary = 'Allow an image to belong to multiple Spree::Variant models' 10 | s.description = 11 | 'Spree makes it tedious to upload the same image across' \ 12 | 'multiple variants. This gem solves the problem.' 13 | s.license = 'BSD-3-Clause' 14 | 15 | s.author = 'Solidus' 16 | s.email = 'contact@solidus.io' 17 | s.homepage = 'https://github.com/solidusio-contrib/solidus_asset_variant_options' 18 | 19 | if s.respond_to?(:metadata) 20 | s.metadata["homepage_uri"] = s.homepage if s.homepage 21 | s.metadata["source_code_uri"] = s.homepage if s.homepage 22 | end 23 | 24 | s.required_ruby_version = ['>= 2.4', '< 4.0'] 25 | 26 | s.files = Dir.chdir(File.expand_path(__dir__)) do 27 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 28 | end 29 | s.test_files = Dir['spec/**/*'] 30 | s.bindir = "exe" 31 | s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) } 32 | s.require_paths = ["lib"] 33 | 34 | s.add_dependency 'solidus_core', ['>= 2.0.0', '< 4'] 35 | s.add_dependency 'solidus_support', '~> 0.8' 36 | 37 | s.add_development_dependency 'solidus_dev_support' 38 | end 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Bonobos, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name Spree nor the names of its contributors may be used to 13 | endorse or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /app/decorators/controllers/spree/admin/images_controller_decorator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Spree 4 | module Admin 5 | module ImagesControllerDecorator 6 | def self.prepended(base) 7 | base.before_action :set_variants, only: %i[create update] 8 | end 9 | 10 | # update variant image positions the same way like image positions 11 | # TODO: find more meaningful way to use variant image positions when showing images 12 | def update_positions 13 | ActiveRecord::Base.transaction do 14 | params[:positions].each do |id, index| 15 | img = Spree::Image.find(id) 16 | img.set_list_position(index) 17 | img.variant_images.each do |vi| 18 | vi.update_attribute(:position, index) 19 | end 20 | end 21 | end 22 | 23 | respond_to do |format| 24 | format.js { head :no_content } 25 | end 26 | end 27 | 28 | private 29 | 30 | def set_variants 31 | if params[:image][:viewable_ids].present? && 32 | params[:image][:viewable_ids].reject(&:blank?).present? 33 | @image.variant_ids = params[:image][:viewable_ids].reject(&:blank?) 34 | else #use master variant as falback when empty 35 | @image.variant_ids = [@product.master.id] 36 | end 37 | end 38 | 39 | def set_viewable # viewable remains first variant 40 | @image.viewable_type = 'Spree::Variant' 41 | @image.viewable_id = @image.variant_ids.first 42 | end 43 | 44 | ::Spree::Admin::ImagesController.prepend self 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Solidus Asset Variant Options 2 | ============================= 3 | 4 | [![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_asset_variant_options.svg?style=svg)](https://circleci.com/gh/solidusio-contrib/solidus_asset_variant_options) 5 | 6 | Adds the ability for admins to use the same image asset for multiple variants. 7 | Originally a fork of [jpdesigndev/spree_asset_variant_options](https://github.com/jpdesigndev/spree_asset_variant_options). 8 | 9 | Installation 10 | ------------ 11 | 12 | Add solidus_asset_variant_options to your Gemfile: 13 | 14 | ```ruby 15 | gem "solidus_asset_variant_options" 16 | ``` 17 | 18 | Bundle your dependencies and run the installation generator: 19 | 20 | ```shell 21 | bundle 22 | bundle exec rails g solidus_asset_variant_options:install 23 | ``` 24 | 25 | Since this extension changes the way images are associated, if you've got any 26 | existing ones you'll need to add the association back in. This can be 27 | accomplished by running: 28 | 29 | ```ruby 30 | Spree::Image.find_each do |image| 31 | if image.viewable_type == "Spree::Variant" 32 | image.variants << image.viewable 33 | end 34 | end 35 | ``` 36 | 37 | Afterwards it is recommended to consolidate the images in the admin. 38 | 39 | Testing 40 | ------- 41 | 42 | First bundle your dependencies, then run `rake`. `rake` will default to 43 | building the dummy app if it does not exist, then it will run specs. The dummy 44 | app can be regenerated by using `rake test_app`. 45 | 46 | ```shell 47 | bundle exec rake 48 | ``` 49 | 50 | When testing your applications integration with this extension you may use it's factories. 51 | Simply add this require statement to your spec_helper: 52 | 53 | ```ruby 54 | require "spree_asset_variant_options/factories" 55 | ``` 56 | --------------------------------------------------------------------------------