spree_core 5.5.0.rc1 → 5.5.0.rc2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '017291169758b3fc4f9791b940c3264c91df65cd9197621ca0cafd8e3ade2ba1'
4
- data.tar.gz: a4f6cfd15c25ce054ce792d545eaf6db7e815b023d66348b4c44d5a226abb3c2
3
+ metadata.gz: 7d9453fadded9ac66f0b64ac5af7ca30a78516088edafa569cd18bcf0b541e1f
4
+ data.tar.gz: 72af3c8a53371f288c2084d2eed1e1f6635d0f2273bcb49f40adc626fe96d4c2
5
5
  SHA512:
6
- metadata.gz: 83d3c2a6c3b13d4c508ab4f4c5c15deb544eb3ba7da48ddd966d872d76ec301f7b87093bc0eb42bd12fe1507575ca09b82919796b079a4aa4e66d71d76dd6332
7
- data.tar.gz: 4453580426836b0b3333309c1eaadabf4bca2fa1fbd8cea51ab1fc1b59c9e02409d098f63e7c77e5dcce222746263d53e021ec3f1b724fd2daa71c623ca573d0
6
+ metadata.gz: 073262b0c991fdbae6c780957052d84ee297abfc6b438b6b1476bc301fc69af8c67ed9904a89e080a14309ac26b86352ad4627f934c0b972c649d520f105c936
7
+ data.tar.gz: 4c581530147dac6e5b3ea79c655a9c739f40f6ce10b55adcdd5abe46d02073a96268e27a64790cc93c607dfe60cc3aef2c62ded74ad4daa3635135ce21847293
@@ -12,6 +12,7 @@ module Spree
12
12
  SCOPES = %w[
13
13
  read_orders write_orders
14
14
  read_products write_products
15
+ read_promotions write_promotions
15
16
  read_customers write_customers
16
17
  read_payments write_payments
17
18
  read_fulfillments write_fulfillments
@@ -20,10 +21,9 @@ module Spree
20
21
  read_store_credits write_store_credits
21
22
  read_stock write_stock
22
23
  read_categories write_categories
23
- read_custom_field_definitions write_custom_field_definitions
24
- read_exports write_exports
25
24
  read_settings write_settings
26
25
  read_webhooks write_webhooks
26
+ read_api_keys write_api_keys
27
27
  read_dashboard
28
28
  read_all write_all
29
29
  ].freeze
@@ -55,8 +55,8 @@ module Spree
55
55
  validates :token_digest, presence: true, uniqueness: true, if: :secret?
56
56
  validates :token_prefix, presence: true, if: :secret?
57
57
  validates :store, presence: true
58
- validates :scopes, presence: true, if: :secret?
59
- validate :validate_known_scopes, if: :secret?
58
+ validates :scopes, presence: true, if: -> { secret? && scopes_enforceable? }
59
+ validate :validate_known_scopes, if: -> { secret? && scopes_enforceable? }
60
60
 
61
61
  before_validation :generate_token, on: :create
62
62
 
@@ -133,6 +133,13 @@ module Spree
133
133
 
134
134
  private
135
135
 
136
+ # Enforce the scope vocabulary on create and whenever scopes change, but
137
+ # not on unrelated updates — revoking a key minted under an older
138
+ # vocabulary (scopes since removed or renamed) must never fail validation.
139
+ def scopes_enforceable?
140
+ new_record? || scopes_changed?
141
+ end
142
+
136
143
  def validate_known_scopes
137
144
  invalid = scopes - SCOPES
138
145
  errors.add(:scopes, "contains unknown scopes: #{invalid.join(', ')}") if invalid.any?
@@ -32,10 +32,10 @@ module Spree
32
32
  end
33
33
 
34
34
  # Returns the current locale.
35
- # Fallback: market default locale -> store default locale.
35
+ # Fallback: market default locale -> store default locale -> I18n default.
36
36
  # @return [String] locale code, e.g. +"en"+, +"de"+
37
37
  def locale
38
- super || market&.default_locale || store&.default_locale
38
+ super || market&.default_locale.presence || store&.default_locale.presence || I18n.default_locale.to_s
39
39
  end
40
40
 
41
41
  # Returns the current tax zone.
@@ -216,6 +216,21 @@ module Spree
216
216
  Spree.export_types
217
217
  end
218
218
 
219
+ # Admin API scope family gating this export type — an export is a bulk
220
+ # read, so an API key needs `read_<required_scope>` to create, view, and
221
+ # download it. Derived from the class name
222
+ # (Spree::Exports::Customers => :customers); override in subclasses whose
223
+ # records are gated by a different scope (e.g. coupon codes =>
224
+ # :promotions). Returns nil on the base class, so unmapped types are
225
+ # only accessible to `read_all`/`write_all` keys.
226
+ #
227
+ # @return [Symbol, nil]
228
+ def required_scope
229
+ return nil if self == Spree::Export
230
+
231
+ to_s.demodulize.underscore.to_sym
232
+ end
233
+
219
234
  def available_models
220
235
  available_types.map(&:model_class)
221
236
  end
@@ -1,6 +1,10 @@
1
1
  module Spree
2
2
  module Exports
3
3
  class CouponCodes < Spree::Export
4
+ def self.required_scope
5
+ :promotions
6
+ end
7
+
4
8
  def csv_headers
5
9
  Spree::CSV::CouponCodePresenter::HEADERS
6
10
  end
@@ -1,6 +1,10 @@
1
1
  module Spree
2
2
  module Exports
3
3
  class NewsletterSubscribers < Spree::Export
4
+ def self.required_scope
5
+ :customers
6
+ end
7
+
4
8
  def scope_includes
5
9
  [:user, { metafields: :metafield_definition }]
6
10
  end
@@ -1,6 +1,10 @@
1
1
  module Spree
2
2
  module Exports
3
3
  class ProductTranslations < Spree::Export
4
+ def self.required_scope
5
+ :products
6
+ end
7
+
4
8
  def scope_includes
5
9
  []
6
10
  end
@@ -13,7 +13,9 @@ module Spree
13
13
  locations = eligible_locations
14
14
  return [] if locations.empty?
15
15
 
16
- ordered = Reducer.new(applicable_rules.to_a, order: order).rank_all(locations)
16
+ ordered = Spree::OrderRouting::Strategy::Reducer
17
+ .new(applicable_rules.to_a, order: order)
18
+ .rank_all(locations)
17
19
  return [] if ordered.empty?
18
20
 
19
21
  packages = build_packages(ordered)
@@ -578,6 +578,7 @@ en:
578
578
  invalid_token: Invalid authentication token.
579
579
  locked: Your account is locked.
580
580
  timeout: Your session expired, please sign in again to continue.
581
+ too_many_attempts: Too many attempts. Please wait a moment and try again.
581
582
  unauthenticated: You need to sign in or sign up before continuing.
582
583
  unconfirmed: You have to confirm your account before continuing.
583
584
  mailer:
@@ -0,0 +1,13 @@
1
+ class ChangeSpreeUserIdentitiesInfoToJsonb < ActiveRecord::Migration[7.2]
2
+ def up
3
+ change_table :spree_user_identities do |t|
4
+ t.change :info, :jsonb, using: 'info::jsonb' if t.respond_to?(:jsonb)
5
+ end
6
+ end
7
+
8
+ def down
9
+ change_table :spree_user_identities do |t|
10
+ t.change :info, :json, using: 'info::json' if t.respond_to?(:jsonb)
11
+ end
12
+ end
13
+ end
@@ -1,10 +1,13 @@
1
+ <%# Decimal columns serialize as strings on the wire (Alba's oj_rails backend
2
+ casts BigDecimal via as_json), so they must be typed :string — only
3
+ integer/float are real JSON numbers. -%>
1
4
  <% typelizable = attributes.reject(&:reference?).select { |a| %i[boolean integer decimal float].include?(a.type) } -%>
2
5
  module Spree
3
6
  module Api
4
7
  module V3
5
8
  class <%= bare_class_name %>Serializer < BaseSerializer
6
9
  <% if typelizable.any? -%>
7
- typelize <%= typelizable.map { |a| "#{a.name}: :#{a.type == :boolean ? 'boolean' : 'number'}" }.join(', ') %>
10
+ typelize <%= typelizable.map { |a| type = a.type == :boolean ? 'boolean' : (a.type == :decimal ? 'string' : 'number'); "#{a.name}: :#{type}" }.join(', ') %>
8
11
 
9
12
  <% end -%>
10
13
  attributes <%= permitted_attribute_names.map { |a| ":#{a}" }.join(', ') %>
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # spree:subscriber — scaffold an event subscriber and register it.
5
+ #
6
+ # bin/rails g spree:subscriber OmsOrderSync order.completed order.canceled
7
+ # bin/rails g spree:subscriber MyApp::BrandSync brand.created brand.updated
8
+ # bin/rails g spree:subscriber CriticalSync order.completed --sync
9
+ #
10
+ # Subscribers are not auto-discovered — they must be appended to the
11
+ # Spree.subscribers array. Forgetting that step produces a silent no-op,
12
+ # so this generator also maintains the registration in
13
+ # config/initializers/spree.rb with an idempotent line per subscriber.
14
+ class SubscriberGenerator < Rails::Generators::NamedBase
15
+ desc 'Creates a Spree event subscriber and registers it in an initializer'
16
+
17
+ # Every Spree app ships config/initializers/spree.rb (created by
18
+ # spree:install) — registrations go there rather than into a second
19
+ # initializer file.
20
+ INITIALIZER_PATH = 'config/initializers/spree.rb'
21
+ REGISTRATION_ANCHOR = "Rails.application.config.after_initialize do\n"
22
+
23
+ argument :events, type: :array, default: [], banner: 'event.name event.name'
24
+
25
+ class_option :sync,
26
+ type: :boolean,
27
+ default: false,
28
+ desc: 'Run the subscriber synchronously (async: false) instead of via ActiveJob'
29
+
30
+ class_option :skip_spec,
31
+ type: :boolean,
32
+ default: false,
33
+ desc: "Don't generate a spec file"
34
+
35
+ def self.source_paths
36
+ [File.expand_path('templates', __dir__), *superclass.source_paths]
37
+ end
38
+
39
+ def create_subscriber_file
40
+ template 'subscriber.rb.tt', File.join('app/subscribers', class_path, "#{subscriber_file_name}.rb")
41
+ end
42
+
43
+ def register_subscriber
44
+ if File.exist?(destination_path(INITIALIZER_PATH))
45
+ content = File.read(destination_path(INITIALIZER_PATH))
46
+ if content.include?(registration_line.strip)
47
+ say_status :identical, "#{INITIALIZER_PATH} (#{subscriber_class_name} already registered)", :blue
48
+ elsif content.include?(REGISTRATION_ANCHOR)
49
+ inject_into_file INITIALIZER_PATH, registration_line, after: REGISTRATION_ANCHOR
50
+ else
51
+ append_to_file INITIALIZER_PATH, registration_block
52
+ end
53
+ else
54
+ create_file INITIALIZER_PATH, registration_block.lstrip
55
+ end
56
+ end
57
+
58
+ def create_spec_file
59
+ return if options[:skip_spec]
60
+
61
+ template 'subscriber_spec.rb.tt', File.join('spec/subscribers', class_path, "#{subscriber_file_name}_spec.rb")
62
+ end
63
+
64
+ def warn_about_missing_events
65
+ return if events.any?
66
+
67
+ say_status :note, "no events given — edit `subscribes_to` in the generated subscriber (e.g. 'order.completed')", :yellow
68
+ end
69
+
70
+ private
71
+
72
+ # "OmsOrderSync" and "OmsOrderSyncSubscriber" both produce
73
+ # OmsOrderSyncSubscriber / oms_order_sync_subscriber.
74
+ def bare_subscriber_name
75
+ base = class_name.demodulize.sub(/Subscriber\z/, '')
76
+ "#{base}Subscriber"
77
+ end
78
+
79
+ def subscriber_file_name
80
+ bare_subscriber_name.underscore
81
+ end
82
+
83
+ def subscriber_class_name
84
+ (class_path.map { |part| part.camelize } + [bare_subscriber_name]).join('::')
85
+ end
86
+
87
+ def namespace_modules
88
+ class_path.map(&:camelize)
89
+ end
90
+
91
+ def subscribes_to_arguments
92
+ listed = events.any? ? events.map { |e| "'#{e}'" } : ["'TODO.replace_with_event_name'"]
93
+ listed << 'async: false' if options[:sync]
94
+ listed.join(', ')
95
+ end
96
+
97
+ def registration_line
98
+ " Spree.subscribers << #{subscriber_class_name}\n"
99
+ end
100
+
101
+ def registration_block
102
+ <<~RUBY
103
+
104
+ # Event subscribers must be registered explicitly — Spree does not
105
+ # auto-discover classes in app/subscribers/.
106
+ Rails.application.config.after_initialize do
107
+ #{registration_line.chomp}
108
+ end
109
+ RUBY
110
+ end
111
+
112
+ def destination_path(relative)
113
+ File.join(destination_root, relative)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,17 @@
1
+ <% namespace_modules.each_with_index do |mod, i| -%>
2
+ <%= ' ' * i %>module <%= mod %>
3
+ <% end -%>
4
+ <% depth = namespace_modules.size -%>
5
+ <%= ' ' * depth %>class <%= bare_subscriber_name %> < Spree::Subscriber
6
+ <%= ' ' * (depth + 1) %>subscribes_to <%= subscribes_to_arguments %>
7
+
8
+ <%= ' ' * (depth + 1) %>def handle(event)
9
+ <%= ' ' * (depth + 2) %># Event payloads carry prefixed IDs — look records up with find_by_prefix_id:
10
+ <%= ' ' * (depth + 2) %>#
11
+ <%= ' ' * (depth + 2) %># record = Spree::Order.find_by_prefix_id(event.payload['id'])
12
+ <%= ' ' * (depth + 2) %># return unless record
13
+ <%= ' ' * (depth + 1) %>end
14
+ <%= ' ' * depth %>end
15
+ <% namespace_modules.each_with_index do |_, i| -%>
16
+ <%= ' ' * (namespace_modules.size - 1 - i) %>end
17
+ <% end -%>
@@ -0,0 +1,9 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe <%= subscriber_class_name %> do
4
+ it 'handles <%= events.first || 'the subscribed event' %>' do
5
+ event = Spree::Event.new(name: <%= (events.first || 'some.event').inspect %>, payload: { 'id' => 'prefixed_id_here' })
6
+
7
+ expect { described_class.new.handle(event) }.not_to raise_error
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.5.0.rc1'.freeze
2
+ VERSION = '5.5.0.rc2'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/tasks/cli.rake CHANGED
@@ -12,8 +12,9 @@ namespace :spree do
12
12
  task create_api_key: :environment do
13
13
  name = ENV.fetch('NAME')
14
14
  key_type = ENV.fetch('KEY_TYPE')
15
+ scopes = ENV.fetch('SCOPES', '').split(',').map(&:strip).reject(&:empty?)
15
16
  store = Spree::Store.default
16
- key = store.api_keys.create!(name: name, key_type: key_type)
17
+ key = store.api_keys.create!(name: name, key_type: key_type, scopes: scopes)
17
18
  print key.plaintext_token
18
19
  end
19
20
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0.rc1
4
+ version: 5.5.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Schofield
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2026-06-10 00:00:00.000000000 Z
13
+ date: 2026-06-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: i18n-tasks
@@ -1565,6 +1565,7 @@ files:
1565
1565
  - db/migrate/20260601000001_create_spree_product_publications.rb
1566
1566
  - db/migrate/20260601000002_add_store_id_to_spree_products.rb
1567
1567
  - db/migrate/20260602000001_add_default_to_spree_channels.rb
1568
+ - db/migrate/20260612000001_change_spree_user_identities_info_to_jsonb.rb
1568
1569
  - db/sample_data/channels.rb
1569
1570
  - db/sample_data/customers.csv
1570
1571
  - db/sample_data/markets.rb
@@ -1612,6 +1613,9 @@ files:
1612
1613
  - lib/generators/spree/model/templates/model.rb.tt
1613
1614
  - lib/generators/spree/model_decorator/model_decorator_generator.rb
1614
1615
  - lib/generators/spree/model_decorator/templates/model_decorator.rb.tt
1616
+ - lib/generators/spree/subscriber/subscriber_generator.rb
1617
+ - lib/generators/spree/subscriber/templates/subscriber.rb.tt
1618
+ - lib/generators/spree/subscriber/templates/subscriber_spec.rb.tt
1615
1619
  - lib/mobility/plugins/store_based_fallbacks.rb
1616
1620
  - lib/spree/analytics.rb
1617
1621
  - lib/spree/core.rb
@@ -1812,9 +1816,9 @@ licenses:
1812
1816
  - BSD-3-Clause
1813
1817
  metadata:
1814
1818
  bug_tracker_uri: https://github.com/spree/spree/issues
1815
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.0.rc1
1819
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.0.rc2
1816
1820
  documentation_uri: https://docs.spreecommerce.org/
1817
- source_code_uri: https://github.com/spree/spree/tree/v5.5.0.rc1
1821
+ source_code_uri: https://github.com/spree/spree/tree/v5.5.0.rc2
1818
1822
  post_install_message:
1819
1823
  rdoc_options: []
1820
1824
  require_paths: