├── .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 |