spree_core 5.4.0.rc4 → 5.4.0.rc4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/concerns/spree/metadata.rb +10 -0
  3. data/app/models/concerns/spree/search_indexable.rb +0 -2
  4. data/app/models/spree/data_feed/google.rb +19 -0
  5. data/app/models/spree/data_feed.rb +47 -0
  6. data/app/models/spree/metafield.rb +1 -1
  7. data/app/models/spree/metafield_definition.rb +1 -1
  8. data/app/models/spree/order/checkout.rb +0 -5
  9. data/app/models/spree/store.rb +2 -0
  10. data/app/models/spree/variant.rb +3 -1
  11. data/app/presenters/spree/data_feeds/base_presenter.rb +23 -0
  12. data/app/presenters/spree/data_feeds/google_presenter.rb +89 -0
  13. data/app/services/spree/sample_data/loader.rb +7 -0
  14. data/app/services/spree/taxons/add_products.rb +3 -1
  15. data/app/services/spree/taxons/regenerate_products.rb +3 -1
  16. data/app/services/spree/taxons/remove_products.rb +3 -1
  17. data/config/locales/en.yml +10 -10
  18. data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +14 -0
  19. data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +5 -0
  20. data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +7 -0
  21. data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +5 -0
  22. data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +5 -0
  23. data/db/sample_data/products.csv +1083 -1083
  24. data/lib/spree/core/engine.rb +5 -0
  25. data/lib/spree/core/version.rb +1 -1
  26. data/lib/spree/core.rb +8 -0
  27. data/lib/spree/testing_support/factories/google_data_feed_factory.rb +7 -0
  28. data/lib/spree/testing_support/factories/product_factory.rb +1 -0
  29. data/spec/fixtures/files/products_import.csv +2 -2
  30. metadata +14 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bc3a50296fda5fe9d3f9cfa5ec4547b44e8dc7c718634f0de73314d55210479
4
- data.tar.gz: 74757285458b4d4a322d5cdc0ea550c4fe7e9ca9de25095fba0371b6c1fb1e93
3
+ metadata.gz: 0445fa1595f6f1abf4bc08b4f961ce9be32e84085e27cf64d0f85ffda00eec7f
4
+ data.tar.gz: 98519e65a1214f7b9a27e95c1cb29ecfcd5faea8b07f453bee23a96be92e2f29
5
5
  SHA512:
6
- metadata.gz: c9ce63a0797b37332944d4d1df8e60f132c27af526e970fd290f993d03b0d9fbf1fcbb1c2c3b55d37184cc10f72b884c79ec68b42398928cbc490a5b55a982c5
7
- data.tar.gz: 14e3357af8ce8cdb75764e15b14759fe4ff37f132a32242b87c269d3137c6f41d11071415be24975c71e4086df5a45b5853840a68dca91ea9541857bd7b18d78
6
+ metadata.gz: cf023f4b0a51304733d7a502bc59df945e3b09c67124f7c8b0578deb8d5f29a1ec3b92bd6cbdbcb3681b818189eb8615342fb25d1c6051e9c85ea5c667af0c53
7
+ data.tar.gz: 8c3a926ca0ac60e7ca2f4e99eae97d2686175095ec414066e04d9d5631d92ca94bf28fb4e6678499cdf7b28994e9d05744eae34c737fd369cfff7fce43ebbbdf
@@ -22,6 +22,16 @@ module Spree
22
22
  self.private_metadata = value
23
23
  end
24
24
 
25
+ def public_metadata=(value)
26
+ unless value.blank? || value == {}
27
+ Spree::Deprecation.warn(
28
+ 'public_metadata is deprecated and will be removed in Spree 6.0. ' \
29
+ 'Use metadata instead. For customer-visible structured data, use metafields with display_on: \'both\'.'
30
+ )
31
+ end
32
+ super
33
+ end
34
+
25
35
  # https://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails
26
36
  class HashSerializer
27
37
  def self.dump(hash)
@@ -45,8 +45,6 @@ module Spree
45
45
  end
46
46
  end
47
47
 
48
- private
49
-
50
48
  def enqueue_search_index
51
49
  return unless search_indexing_enabled?
52
50
 
@@ -0,0 +1,19 @@
1
+ require_dependency 'spree/data_feed'
2
+
3
+ module Spree
4
+ class DataFeed::Google < DataFeed
5
+ class << self
6
+ def label
7
+ 'Google Merchant Center Feed'
8
+ end
9
+
10
+ def provider_name
11
+ 'google'
12
+ end
13
+
14
+ def presenter_class
15
+ Spree::DataFeeds::GooglePresenter
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ module Spree
2
+ class DataFeed < Spree.base_class
3
+ has_prefix_id :df
4
+
5
+ belongs_to :store, class_name: 'Spree::Store', foreign_key: 'store_id'
6
+
7
+ scope :for_store, ->(store) { where(store: store) }
8
+ scope :active, -> { where(active: true) }
9
+
10
+ before_validation :generate_slug
11
+
12
+ with_options presence: true do
13
+ validates :store
14
+ validates :name, uniqueness: true
15
+ validates :slug, uniqueness: { scope: :store_id }
16
+ end
17
+
18
+ def formatted_url
19
+ "#{store.formatted_url}/api/v3/store/feeds/#{slug}.xml"
20
+ end
21
+
22
+ private
23
+
24
+ def generate_slug
25
+ new_slug = slug.blank? ? SecureRandom.uuid : slug.parameterize
26
+ write_attribute(:slug, new_slug)
27
+ end
28
+
29
+ class << self
30
+ def label
31
+ raise NotImplementedError
32
+ end
33
+
34
+ def provider_name
35
+ raise NotImplementedError
36
+ end
37
+
38
+ def presenter_class
39
+ raise NotImplementedError
40
+ end
41
+
42
+ def available_types
43
+ Spree.data_feed_types
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class Metafield < Spree.base_class
3
- has_prefix_id :mf
3
+ has_prefix_id :cf
4
4
 
5
5
  #
6
6
  # Associations
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class MetafieldDefinition < Spree.base_class
3
- has_prefix_id :mfdef
3
+ has_prefix_id :cfdef
4
4
 
5
5
  include Spree::DisplayOn
6
6
 
@@ -122,7 +122,6 @@ module Spree
122
122
  after_transition to: :complete, do: :use_all_coupon_codes
123
123
  after_transition to: :complete, do: :redeem_gift_card
124
124
  after_transition to: :complete, do: :subscribe_to_newsletter
125
- after_transition to: :complete, do: :publish_order_completed_event
126
125
 
127
126
  after_transition from: any - :cart, to: any - [:confirm, :complete] do |order|
128
127
  order.update_totals
@@ -133,10 +132,6 @@ module Spree
133
132
  alias_method :save_state, :save
134
133
  end
135
134
 
136
- def publish_order_completed_event
137
- publish_event('order.completed')
138
- end
139
-
140
135
  def subscribe_to_newsletter
141
136
  return unless accept_marketing?
142
137
 
@@ -91,6 +91,8 @@ module Spree
91
91
 
92
92
  has_many :wishlists, class_name: 'Spree::Wishlist'
93
93
 
94
+ has_many :data_feeds, class_name: 'Spree::DataFeed'
95
+
94
96
  belongs_to :default_country, class_name: 'Spree::Country'
95
97
  belongs_to :checkout_zone, class_name: 'Spree::Zone'
96
98
 
@@ -292,9 +292,11 @@ module Spree
292
292
  end
293
293
 
294
294
  # Returns first Image for Variant.
295
+ # @deprecated Use #primary_media instead.
295
296
  # @return [Spree::Image, nil]
296
297
  def primary_image
297
- images.first
298
+ Spree::Deprecation.warn('Spree::Variant#primary_image is deprecated and will be removed in Spree 6.0. Please use Spree::Variant#primary_media instead.')
299
+ primary_media
298
300
  end
299
301
 
300
302
  # Returns second Image for Variant (for hover effects).
@@ -0,0 +1,23 @@
1
+ module Spree
2
+ module DataFeeds
3
+ class BasePresenter
4
+ def initialize(data_feed)
5
+ @data_feed = data_feed
6
+ @store = data_feed.store
7
+ end
8
+
9
+ attr_reader :data_feed, :store
10
+
11
+ # @return [String] the feed content (XML, CSV, etc.)
12
+ def call
13
+ raise NotImplementedError
14
+ end
15
+
16
+ private
17
+
18
+ def products
19
+ store.products.active
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ require 'nokogiri'
2
+
3
+ module Spree
4
+ module DataFeeds
5
+ class GooglePresenter < BasePresenter
6
+ # @return [String] RSS XML feed for Google Merchant Center
7
+ def call
8
+ builder = Nokogiri::XML::Builder.new do |xml|
9
+ xml.rss('xmlns:g' => 'http://base.google.com/ns/1.0', 'version' => '2.0') do
10
+ xml.channel do
11
+ build_store_info(xml)
12
+ build_items(xml)
13
+ end
14
+ end
15
+ end
16
+
17
+ builder.to_xml
18
+ end
19
+
20
+ private
21
+
22
+ def build_store_info(xml)
23
+ xml.title store.name
24
+ xml.link store.storefront_url
25
+ xml.description store.meta_description
26
+ end
27
+
28
+ def build_items(xml)
29
+ products.includes(:primary_media, public_metafields: :metafield_definition, variants_including_master: [:primary_media, option_values: :option_type]).find_each do |product|
30
+ product.variants_including_master.active.each do |variant|
31
+ next if variant.is_master? && product.has_variants?
32
+
33
+ build_item(xml, product, variant)
34
+ end
35
+ end
36
+ end
37
+
38
+ def build_item(xml, product, variant)
39
+ image_url = image_link(variant, product)
40
+ return if image_url.nil?
41
+
42
+ xml.item do
43
+ build_required_attributes(xml, product, variant, image_url)
44
+ build_optional_attributes(xml, product)
45
+ end
46
+ end
47
+
48
+ def build_required_attributes(xml, product, variant, image_url)
49
+ xml['g'].id variant.id
50
+ xml['g'].item_group_id product.id
51
+ xml['g'].title format_title(product, variant)
52
+ xml['g'].description product.description || format_title(product, variant)
53
+ xml['g'].link "#{store.storefront_url}/products/#{product.slug}"
54
+ xml['g'].image_link image_url
55
+ xml['g'].price "#{variant.price} #{variant.cost_currency}"
56
+ xml['g'].availability availability(product)
57
+ xml['g'].availability_date product.available_on.xmlschema if product.available_on.present?
58
+ end
59
+
60
+ def build_optional_attributes(xml, product)
61
+ product.public_metafields.each do |metafield|
62
+ xml['g'].send(metafield.metafield_definition.key.parameterize.underscore, metafield.value)
63
+ end
64
+ end
65
+
66
+ def format_title(product, variant)
67
+ parts = [product.name]
68
+ variant.option_values.each do |option_value|
69
+ parts << option_value.name
70
+ end
71
+ parts.join(' - ')
72
+ end
73
+
74
+ def image_link(variant, product)
75
+ image = variant.primary_media || product.primary_media
76
+ return if image.nil?
77
+
78
+ Rails.application.routes.url_helpers.cdn_image_url(image.attachment.variant(:xlarge))
79
+ end
80
+
81
+ def availability(product)
82
+ return 'in stock' if product.available? && (product.available_on.nil? || product.available_on.past?)
83
+ return 'backorder' if product.backorderable? && product.backordered? && product.available_on&.future?
84
+
85
+ 'out of stock'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -26,6 +26,8 @@ module Spree
26
26
  puts 'Loading sample posts...'
27
27
  load_ruby_file('posts')
28
28
 
29
+ clear_jobs_queue
30
+
29
31
  puts 'Sample data loaded successfully!'
30
32
  end
31
33
  end
@@ -73,6 +75,11 @@ module Spree
73
75
  ensure
74
76
  Spree::Config[:geocode_addresses] = previous
75
77
  end
78
+
79
+ def clear_jobs_queue
80
+ adapter = ActiveJob::Base.queue_adapter
81
+ adapter.enqueued_jobs.clear if adapter.respond_to?(:enqueued_jobs)
82
+ end
76
83
  end
77
84
  end
78
85
  end
@@ -34,8 +34,10 @@ module Spree
34
34
  taxon_ids.each { |id| Spree::Taxon.reset_counters(id, :classifications) }
35
35
  product_ids.each { |id| Spree::Product.reset_counters(id, :classifications) }
36
36
 
37
- # clearing cache
37
+ # clear cache & index products
38
38
  Spree::Product.where(id: product_ids).touch_all
39
+ products.each(&:enqueue_search_index)
40
+
39
41
  Spree::Taxon.where(id: taxon_ids).touch_all
40
42
  Spree::Taxons::TouchFeaturedSections.call(taxon_ids: taxon_ids) if defined?(Spree::Taxons::TouchFeaturedSections)
41
43
 
@@ -32,7 +32,9 @@ module Spree
32
32
  Spree::Classification.insert_all(records_to_insert)
33
33
 
34
34
  # expire product cache
35
- Spree::Product.where(id: (previous_products_ids + product_ids_to_insert).uniq).touch_all
35
+ products = Spree::Product.where(id: (previous_products_ids + product_ids_to_insert).uniq)
36
+ products.touch_all
37
+ products.each(&:enqueue_search_index)
36
38
  end
37
39
 
38
40
  # update counter caches
@@ -49,8 +49,10 @@ module Spree
49
49
  taxon_ids.each { |id| Spree::Taxon.reset_counters(id, :classifications) }
50
50
  product_ids.each { |id| Spree::Product.reset_counters(id, :classifications) }
51
51
 
52
- # clear cache
52
+ # clear cache & index products
53
53
  Spree::Product.where(id: product_ids).touch_all
54
+ products.each(&:enqueue_search_index)
55
+
54
56
  Spree::Taxon.where(id: taxon_ids).touch_all
55
57
  Spree::Taxons::TouchFeaturedSections.call(taxon_ids: taxon_ids) if defined?(Spree::Taxons::TouchFeaturedSections)
56
58
 
@@ -532,11 +532,11 @@ en:
532
532
  one: Tax Rate
533
533
  other: Tax Rates
534
534
  spree/taxon:
535
- one: Taxon
536
- other: Taxons
535
+ one: Category
536
+ other: Categories
537
537
  spree/taxonomy:
538
- one: Taxonomy
539
- other: Taxonomies
538
+ one: Category
539
+ other: Categories
540
540
  spree/theme:
541
541
  one: Theme
542
542
  other: Themes
@@ -1451,7 +1451,7 @@ en:
1451
1451
  new_image: New Image
1452
1452
  new_line_item: Add line item
1453
1453
  new_metafield_definition: New Metafield Definition
1454
- new_nested_taxon: New nested taxon
1454
+ new_nested_taxon: New nested subcategory
1455
1455
  new_option_type: New Option Type
1456
1456
  new_order: New Order
1457
1457
  new_order_completed: New Order Completed
@@ -1486,8 +1486,8 @@ en:
1486
1486
  new_store_credit_category: New Store Credit Category
1487
1487
  new_tax_category: New Tax Category
1488
1488
  new_tax_rate: New Tax Rate
1489
- new_taxon: New Taxon
1490
- new_taxonomy: New Taxonomy
1489
+ new_taxon: New Subcategory
1490
+ new_taxonomy: New Category
1491
1491
  new_tracker: New Tracker
1492
1492
  new_user: New User
1493
1493
  new_variant: New Variant
@@ -2332,15 +2332,15 @@ en:
2332
2332
  label: Order must contain x amount of these taxons
2333
2333
  match_all: all
2334
2334
  match_any: at least one
2335
- taxonomies: Taxonomies
2336
- taxonomy: Taxonomy
2335
+ taxonomies: Categories
2336
+ taxonomy: Category
2337
2337
  taxonomy_brands_name: Brands
2338
2338
  taxonomy_categories_name: Categories
2339
2339
  taxonomy_collections_name: Collections
2340
2340
  taxonomy_edit: Edit taxonomy
2341
2341
  taxonomy_tree_error: The requested change has not been accepted and the tree has been returned to its previous state, please try again.
2342
2342
  taxonomy_tree_instruction: "* Right click (or double tap on iOS device) a child in the tree to access the menu for adding, deleting or sorting a child."
2343
- taxons: Taxons
2343
+ taxons: Categories
2344
2344
  terms_of_service: Terms of Service
2345
2345
  test: Test
2346
2346
  test_mode: Test Mode
@@ -0,0 +1,14 @@
1
+ class CreateSpreeDataFeedSettings < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :spree_data_feed_settings do |t|
4
+ t.references :spree_store
5
+
6
+ t.string :name
7
+ t.string :provider
8
+ t.string :uuid, unique: true
9
+ t.boolean :enabled, default: true
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class RenameDataFeedSettingsTable < ActiveRecord::Migration[6.1]
2
+ def change
3
+ rename_table :spree_data_feed_settings, :spree_data_feeds
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class RenameDataFeedTableColumns < ActiveRecord::Migration[6.1]
2
+ def change
3
+ rename_column :spree_data_feeds, :spree_store_id, :store_id
4
+ rename_column :spree_data_feeds, :enabled, :active
5
+ rename_column :spree_data_feeds, :uuid, :slug
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class AddIndexesToDataFeedsTable < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_index :spree_data_feeds, [:store_id, :slug, :provider]
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class RenameDataFeedsColumnProviderToType < ActiveRecord::Migration[6.1]
2
+ def change
3
+ rename_column :spree_data_feeds, :provider, :type
4
+ end
5
+ end