spree_google_products 1.0

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +177 -0
  3. data/Rakefile +21 -0
  4. data/app/assets/config/spree_google_products_manifest.js +5 -0
  5. data/app/assets/images/app_icons/google_logo.svg +1 -0
  6. data/app/assets/images/app_icons/google_merchant_logo.svg +52 -0
  7. data/app/assets/images/app_icons/welcome_scene.svg +1 -0
  8. data/app/controllers/spree/admin/google_merchant_settings_controller.rb +182 -0
  9. data/app/controllers/spree/admin/google_shopping/dashboard_controller.rb +64 -0
  10. data/app/controllers/spree/admin/google_shopping/issues_controller.rb +42 -0
  11. data/app/controllers/spree/admin/google_shopping/products_controller.rb +79 -0
  12. data/app/controllers/spree/admin/google_shopping/taxons_controller.rb +39 -0
  13. data/app/helpers/spree/admin/google_shopping_helper.rb +39 -0
  14. data/app/javascript/spree_google_products/application.js +16 -0
  15. data/app/javascript/spree_google_products/controllers/spree_google_products_controller.js +7 -0
  16. data/app/jobs/spree/google_shopping/fetch_status_job.rb +23 -0
  17. data/app/jobs/spree/google_shopping/sync_all_job.rb +18 -0
  18. data/app/jobs/spree/google_shopping/sync_product_job.rb +35 -0
  19. data/app/jobs/spree_google_products/base_job.rb +5 -0
  20. data/app/models/spree/google_credential.rb +29 -0
  21. data/app/models/spree/google_product_attribute.rb +9 -0
  22. data/app/models/spree/google_taxon.rb +5 -0
  23. data/app/models/spree/google_variant_attribute.rb +6 -0
  24. data/app/models/spree/product_decorator.rb +29 -0
  25. data/app/models/spree/store_decorator.rb +9 -0
  26. data/app/models/spree/variant_decorator.rb +8 -0
  27. data/app/models/spree_google_products/ability.rb +10 -0
  28. data/app/services/spree/google_shopping/content_service.rb +215 -0
  29. data/app/services/spree/google_shopping/status_service.rb +150 -0
  30. data/app/services/spree/google_token_service.rb +59 -0
  31. data/app/views/spree/admin/google_merchant_settings/edit.html.erb +331 -0
  32. data/app/views/spree/admin/google_shopping/dashboard/index.html.erb +121 -0
  33. data/app/views/spree/admin/google_shopping/issues/index.html.erb +106 -0
  34. data/app/views/spree/admin/google_shopping/products/_filters.html.erb +19 -0
  35. data/app/views/spree/admin/google_shopping/products/edit.html.erb +336 -0
  36. data/app/views/spree/admin/google_shopping/products/index.html.erb +131 -0
  37. data/app/views/spree/admin/google_shopping/products/issues.html.erb +48 -0
  38. data/app/views/spree/admin/products/google_shopping.html.erb +63 -0
  39. data/app/views/spree_google_products/_head.html.erb +1 -0
  40. data/config/importmap.rb +6 -0
  41. data/config/initializers/assets.rb +8 -0
  42. data/config/initializers/force_encryption_keys.rb +24 -0
  43. data/config/initializers/spree.rb +32 -0
  44. data/config/initializers/spree_google_products.rb +29 -0
  45. data/config/locales/en.yml +35 -0
  46. data/config/routes.rb +33 -0
  47. data/db/migrate/20260112000000_add_spree_google_shopping_tables.rb +89 -0
  48. data/lib/generators/spree_google_products/install/install_generator.rb +84 -0
  49. data/lib/generators/spree_google_products/uninstall/uninstall_generator.rb +55 -0
  50. data/lib/spree_google_products/configuration.rb +13 -0
  51. data/lib/spree_google_products/engine.rb +37 -0
  52. data/lib/spree_google_products/factories.rb +6 -0
  53. data/lib/spree_google_products/version.rb +7 -0
  54. data/lib/spree_google_products.rb +13 -0
  55. data/lib/tasks/spree_google_products.rake +41 -0
  56. metadata +190 -0
@@ -0,0 +1,63 @@
1
+ <%= render 'spree/admin/shared/product_tabs', current: :google_shopping %>
2
+
3
+ <% content_for :page_title do %>
4
+ Google Shopping Attributes
5
+ <% end %>
6
+
7
+ <%= form_for [:admin, @product], url: admin_product_path(@product), method: :put do |f| %>
8
+ <div class="card mb-3">
9
+ <div class="card-header">
10
+ <h5 class="card-title mb-0">Basic Information</h5>
11
+ </div>
12
+ <div class="card-body">
13
+ <%= f.fields_for :google_product_attribute do |g| %>
14
+
15
+ <div class="form-group">
16
+ <%= g.label :brand, "Brand Name" %>
17
+ <%= g.text_field :brand, class: 'form-control' %>
18
+ </div>
19
+
20
+ <div class="row">
21
+ <div class="col-6">
22
+ <div class="form-group">
23
+ <%= g.label :gtin, "GTIN / UPC / EAN" %>
24
+ <%= g.text_field :gtin, class: 'form-control' %>
25
+ <small class="form-text text-muted">Leave empty if custom product.</small>
26
+ </div>
27
+ </div>
28
+ <div class="col-6">
29
+ <div class="form-group">
30
+ <%= g.label :mpn, "MPN (Manufacturer Part Number)" %>
31
+ <%= g.text_field :mpn, class: 'form-control' %>
32
+ </div>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="row">
37
+ <div class="col-4">
38
+ <div class="form-group">
39
+ <%= g.label :condition %>
40
+ <%= g.select :condition, ['new', 'refurbished', 'used'], {}, class: 'select2' %>
41
+ </div>
42
+ </div>
43
+ <div class="col-4">
44
+ <div class="form-group">
45
+ <%= g.label :gender %>
46
+ <%= g.select :gender, ['', 'male', 'female', 'unisex'], {}, class: 'select2' %>
47
+ </div>
48
+ </div>
49
+ <div class="col-4">
50
+ <div class="form-group">
51
+ <%= g.label :age_group %>
52
+ <%= g.select :age_group, ['', 'adult', 'kids', 'toddler', 'infant'], {}, class: 'select2' %>
53
+ </div>
54
+ </div>
55
+ end %>
56
+ <% end %>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="form-actions" data-hook="buttons">
61
+ <%= render partial: 'spree/admin/shared/edit_resource_links' %>
62
+ </div>
63
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= javascript_import_module_tag 'application-spree-google-products' %>
@@ -0,0 +1,6 @@
1
+ pin 'application-spree-google-products', to: 'spree_google_products/application.js', preload: false
2
+
3
+ pin_all_from SpreeGoogleProducts::Engine.root.join('app/javascript/spree_google_products/controllers'),
4
+ under: 'spree_google_products/controllers',
5
+ to: 'spree_google_products/controllers',
6
+ preload: 'application-spree-google-products'
@@ -0,0 +1,8 @@
1
+ # Only run this if Sprockets is loaded (Spree 4.x / 5.0)
2
+ if Rails.configuration.respond_to?(:assets) && defined?(Sprockets)
3
+ Rails.application.config.assets.precompile += %w[
4
+ app_icons/welcome_scene.svg
5
+ app_icons/google_logo.svg
6
+ app_icons/google_merchant_logo.svg
7
+ ]
8
+ end
@@ -0,0 +1,24 @@
1
+ if Rails.env.development? || Rails.env.test?
2
+ env_file = Rails.root.join('.env')
3
+
4
+ if File.exist?(env_file)
5
+ File.foreach(env_file) do |line|
6
+ next if line.strip.start_with?('#') || line.strip.empty?
7
+ key, value = line.strip.split('=', 2)
8
+ if key && value
9
+ ENV[key] = value
10
+ end
11
+ end
12
+ end
13
+
14
+ if ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present?
15
+ ActiveRecord::Encryption.configure(
16
+ primary_key: ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'],
17
+ deterministic_key: ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'],
18
+ key_derivation_salt: ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT']
19
+ )
20
+ puts "🔐 Active Record Encryption Keys Loaded Successfully via Initializer."
21
+ else
22
+ puts "⚠️ WARNING: Active Record Encryption Keys are MISSING in .env"
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ # Uncomment lines below to add your own custom business logic
2
+ # such as promotions, shipping methods, etc.
3
+ Rails.application.config.after_initialize do
4
+ # Spree.shipping_methods << Spree::ShippingMethods::SuperExpensiveNotVeryFastShipping
5
+ # Spree.payment_methods << Spree::PaymentMethods::VerySafeAndReliablePaymentMethod
6
+
7
+ # Spree.calculators.tax_rates << Spree::TaxRates::FinanceTeamForcedMeToCodeThis
8
+
9
+ # Spree.stock_splitters << Spree::Stock::Splitters::SecretLogicSplitter
10
+
11
+ # Spree.adjusters << Spree::Adjustable::Adjuster::TaxTheRich
12
+
13
+ # Custom promotions
14
+ # Spree.calculators.promotion_actions_create_adjustments << Spree::Calculators::PromotionActions::CreateAdjustments::AddDiscountForFriends
15
+ # Spree.calculators.promotion_actions_create_item_adjustments << Spree::Calculators::PromotionActions::CreateItemAdjustments::FinanceTeamForcedMeToCodeThis
16
+ # Spree.promotions.rules << Spree::Promotions::Rules::OnlyForVIPCustomers
17
+ # Spree.promotions.actions << Spree::Promotions::Actions::GiftWithPurchase
18
+
19
+ # Spree.taxon_rules << Spree::TaxonRules::ProductsWithColor
20
+
21
+ # Spree.exports << Spree::Exports::Payments
22
+ # Spree.reports << Spree::Reports::MassivelyOvercomplexReportForCfo
23
+
24
+ # Themes and page builder
25
+ # Spree.page_builder.themes << Spree::Themes::NewShinyTheme
26
+ # Spree.page_builder.theme_layout_sections << Spree::PageSections::SuperImportantCeoBio
27
+ # Spree.page_builder.page_sections << Spree::PageSections::ContactFormToGetInTouch
28
+ # Spree.page_builder.page_blocks << Spree::PageBlocks::BigRedButtonToCallSales
29
+
30
+ # Storefront partials
31
+ Spree.storefront.partials.head << 'spree_google_products/head'
32
+ end
@@ -0,0 +1,29 @@
1
+ Rails.application.config.after_initialize do
2
+ if Spree.respond_to?(:admin) && Spree.admin.respond_to?(:navigation)
3
+ sidebar = Spree.admin.navigation.sidebar
4
+ sidebar.add :google_shopping,
5
+ label: 'Google Shopping',
6
+ icon: 'brand-google',
7
+ url: :admin_google_shopping_dashboard_path,
8
+ position: 85
9
+ sidebar.add :google_dashboard,
10
+ parent: :google_shopping,
11
+ label: 'Dashboard',
12
+ url: :admin_google_shopping_dashboard_path,
13
+ active: -> { params[:controller].include?('google_shopping/dashboard') }
14
+ sidebar.add :google_products,
15
+ parent: :google_shopping,
16
+ label: 'Products',
17
+ url: :admin_google_shopping_products_path,
18
+ active: -> { params[:controller].include?('google_shopping/products') }
19
+ sidebar.add :google_settings,
20
+ parent: :google_shopping,
21
+ label: :settings,
22
+ url: :edit_admin_google_merchant_settings_path,
23
+ active: -> { controller_name == 'google_merchant_settings' }
24
+ end
25
+
26
+ if defined?(Spree::Ability) && defined?(SpreeGoogleProducts::Ability)
27
+ Spree::Ability.register_ability(SpreeGoogleProducts::Ability)
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ en:
2
+ spree:
3
+ # --- Page Titles & Headers ---
4
+ google_merchant_settings: "Google Merchant Settings"
5
+ google_shopping_settings: "Google Shopping Settings"
6
+ google_merchant: "Merchant Center"
7
+ google_merchant_products: "Google Products"
8
+ google_data_quality_issues: "Data Quality Issues"
9
+ google_shopping: "Google Shopping"
10
+
11
+ # --- Navigation & Tabs ---
12
+ admin:
13
+ tab:
14
+ google_shopping: "Google Shopping"
15
+
16
+ # --- Sidebar Menu ---
17
+ google_dashboard: "Dashboard"
18
+ google_products: "Products"
19
+ # 'settings' is already provided by Spree core
20
+
21
+ # --- Actions & Flash Messages ---
22
+ connect_merchant_center: "Connect Merchant Center"
23
+ sync_now: "Sync Now"
24
+ sync_all_products: "Sync All Products"
25
+ view_issues: "View Issues"
26
+ view_fixes: "View Fixes"
27
+
28
+ # --- Status Labels ---
29
+ # Used in Dashboard and Product Lists
30
+ google_status:
31
+ active: "Active"
32
+ pending: "Pending"
33
+ disapproved: "Disapproved"
34
+ limited: "Limited"
35
+ not_synced: "Not Synced"
data/config/routes.rb ADDED
@@ -0,0 +1,33 @@
1
+ Spree::Core::Engine.add_routes do
2
+ namespace :admin do
3
+ # Dashboard
4
+ namespace :google_shopping do
5
+ get 'dashboard', to: 'dashboard#index', as: :dashboard
6
+ resources :issues, only: [:index]
7
+ post 'sync', to: 'dashboard#sync', as: :sync
8
+
9
+ # Dedicated Product Manager
10
+ resources :products, only: [:index, :edit, :update] do
11
+ member do
12
+ post :sync_one
13
+ get :issues
14
+ end
15
+ end
16
+
17
+ # Taxonomy Search
18
+ resources :taxons, only: [] do
19
+ collection do
20
+ get :drill_down
21
+ end
22
+ end
23
+ end
24
+
25
+ # Settings Routes
26
+ resource :google_merchant_settings, only: [:edit, :update] do
27
+ get :connect
28
+ get :callback
29
+ delete :disconnect
30
+ post :sync_now
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,89 @@
1
+ class AddSpreeGoogleShoppingTables < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :spree_google_credentials do |t|
4
+ t.references :store, null: false, index: true
5
+
6
+ # Auth & Security
7
+ t.string :merchant_center_id
8
+ t.string :ad_account_id
9
+ t.text :access_token
10
+ t.text :refresh_token
11
+ t.string :scope
12
+ t.string :email
13
+ t.datetime :token_expires_at
14
+ t.datetime :last_sync_at
15
+
16
+ # Targeting Configuration
17
+ t.string :target_country, default: 'US'
18
+ t.string :target_currency, default: 'USD'
19
+
20
+ # Global Defaults
21
+ t.string :default_google_product_category
22
+ t.string :default_product_type
23
+ t.integer :default_min_handling_time
24
+ t.integer :default_max_handling_time
25
+
26
+ t.timestamps
27
+ end
28
+
29
+ create_table :spree_google_product_attributes do |t|
30
+ t.references :product, null: false, index: true
31
+
32
+ # Core Attributes
33
+ t.string :brand
34
+ t.string :gtin
35
+ t.string :mpn
36
+ t.boolean :identifier_exists, default: true
37
+
38
+ # Categorization
39
+ t.string :google_product_category
40
+ t.string :product_type
41
+
42
+ # Demographics
43
+ t.string :condition, default: 'new'
44
+ t.string :gender
45
+ t.string :age_group
46
+ t.string :color
47
+ t.string :size
48
+ t.string :material
49
+
50
+ # Status & Feedback
51
+ t.string :google_status
52
+ t.text :google_issues
53
+
54
+ # Extra Data
55
+ t.string :custom_label_0
56
+
57
+ # Sale Logic
58
+ t.datetime :sale_start_at
59
+ t.datetime :sale_end_at
60
+
61
+ # Shipping Overrides
62
+ t.integer :min_handling_time
63
+ t.integer :max_handling_time
64
+
65
+ t.timestamps
66
+ end
67
+
68
+ create_table :spree_google_variant_attributes do |t|
69
+ t.references :variant, null: false, index: true
70
+
71
+ # Identification Overrides
72
+ t.string :gtin
73
+ t.string :mpn
74
+
75
+ # Status Syncing
76
+ t.string :google_status, index: true
77
+ t.text :google_issues
78
+ t.datetime :last_synced_at
79
+
80
+ t.timestamps
81
+ end
82
+
83
+ create_table :spree_google_taxons do |t|
84
+ t.string :name, index: true
85
+ t.string :google_id, index: true
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,84 @@
1
+ require 'securerandom'
2
+
3
+ module SpreeGoogleProducts
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ class_option :auto_run_migrations, type: :boolean, default: false
9
+
10
+ def add_migrations
11
+ run 'bundle exec rake railties:install:migrations FROM=spree_google_products'
12
+ end
13
+
14
+ def create_initializer
15
+ initializer_content = <<~RUBY
16
+ if Rails.env.development? || Rails.env.test?
17
+ env_file = Rails.root.join('.env')
18
+
19
+ if File.exist?(env_file)
20
+ File.foreach(env_file) do |line|
21
+ next if line.strip.start_with?('#') || line.strip.empty?
22
+ key, value = line.strip.split('=', 2)
23
+ ENV[key] = value if key && value
24
+ end
25
+ end
26
+
27
+ if ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present?
28
+ ActiveRecord::Encryption.configure(
29
+ primary_key: ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'],
30
+ deterministic_key: ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'],
31
+ key_derivation_salt: ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT']
32
+ )
33
+ end
34
+ end
35
+ RUBY
36
+
37
+ create_file "config/initializers/force_encryption_keys.rb", initializer_content
38
+ end
39
+
40
+ def setup_encryption_keys
41
+ return unless Rails.env.development? || Rails.env.test?
42
+ env_file = File.join(Rails.root, '.env')
43
+ create_file '.env' unless File.exist?(env_file)
44
+ current_content = File.read(env_file)
45
+ keys_to_add = []
46
+
47
+ {
48
+ "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY" => SecureRandom.alphanumeric(32),
49
+ "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY" => SecureRandom.alphanumeric(32),
50
+ "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT" => SecureRandom.alphanumeric(32)
51
+ }.each do |key_name, value|
52
+ unless current_content.include?(key_name)
53
+ keys_to_add << "#{key_name}=#{value}"
54
+ end
55
+ end
56
+
57
+ if keys_to_add.any?
58
+ append_to_file '.env', "\n# --- Added by Spree Google Shopping Installer ---\n#{keys_to_add.join("\n")}\n"
59
+ puts "🔑 Added missing Active Record Encryption keys to .env"
60
+ else
61
+ puts "✅ Active Record Encryption keys already present."
62
+ end
63
+ end
64
+
65
+ def run_migrations
66
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
67
+ if run_migrations
68
+ run 'bundle exec rails db:migrate'
69
+ else
70
+ puts 'Skipping rails db:migrate, don\'t forget to run it!'
71
+ end
72
+ end
73
+
74
+ def run_seeds
75
+ run_seeds = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to download and seed Google Taxonomies now? (Recommended) [Y/n]'))
76
+ if run_seeds
77
+ run 'bundle exec rake spree_google_shopping:seed_taxons'
78
+ else
79
+ puts 'Skipping seed. You can run it later with: bundle exec rake spree_google_shopping:seed_taxons'
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,55 @@
1
+ module SpreeGoogleProducts
2
+ module Generators
3
+ class UninstallGenerator < Rails::Generators::Base
4
+
5
+ def drop_tables
6
+ return unless yes?("WARNING: This will destroy all Google Shopping data (Credentials, Product Settings, Taxonomies). Are you sure? [y/N]")
7
+
8
+ tables = %w[spree_google_credentials spree_google_product_attributes spree_google_variant_attributes spree_google_taxons]
9
+
10
+ tables.each do |table|
11
+ if ActiveRecord::Base.connection.table_exists?(table)
12
+ ActiveRecord::Base.connection.drop_table(table, force: :cascade)
13
+ puts "🔥 Dropped table: #{table}"
14
+ else
15
+ puts " Table #{table} does not exist, skipping."
16
+ end
17
+ end
18
+ end
19
+
20
+ def remove_migrations
21
+ migration_files = Dir.glob("db/migrate/*add_spree_google_shopping_tables.spree_google_products.rb")
22
+ migration_files += Dir.glob("db/migrate/*spree_google_products.rb")
23
+
24
+ if migration_files.any?
25
+ migration_files.uniq.each do |file|
26
+ version = File.basename(file).split('_').first
27
+ if version.match?(/^\d+$/)
28
+ ActiveRecord::Base.connection.execute("DELETE FROM schema_migrations WHERE version = '#{version}'")
29
+ puts "🧹 Removed version #{version} from schema_migrations."
30
+ end
31
+
32
+ remove_file file
33
+ end
34
+ else
35
+ puts "⚠️ No specific migration files found to remove."
36
+ end
37
+ end
38
+
39
+ def remove_initializer
40
+ remove_file "config/initializers/force_encryption_keys.rb"
41
+ end
42
+
43
+ def clean_env_reminder
44
+ puts "\n"
45
+ puts "⚠️ IMPORTANT: We did not remove the Encryption Keys from your .env file."
46
+ puts " If you are not using them for other plugins, please manually remove:"
47
+ puts " - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY"
48
+ puts " - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY"
49
+ puts " - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT"
50
+ puts "\n"
51
+ puts "✅ Spree Google Products has been uninstalled."
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,13 @@
1
+ module SpreeGoogleProducts
2
+ class Configuration < Spree::Preferences::Configuration
3
+
4
+ # Some example preferences are shown below, for more information visit:
5
+ # https://docs.spreecommerce.org/developer/contributing/creating-an-extension
6
+
7
+ # preference :enabled, :boolean, default: true
8
+ # preference :dark_chocolate, :boolean, default: true
9
+ # preference :color, :string, default: 'Red'
10
+ # preference :favorite_number, :integer
11
+ # preference :supported_locales, :array, default: [:en]
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module SpreeGoogleProducts
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_google_products'
6
+
7
+ # use rspec for tests
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ end
11
+
12
+ initializer 'spree_google_products.environment', before: :load_config_initializers do |_app|
13
+ SpreeGoogleProducts::Config = SpreeGoogleProducts::Configuration.new
14
+ end
15
+
16
+ initializer 'spree_google_products.assets' do |app|
17
+ app.config.assets.paths << root.join('app/javascript')
18
+ app.config.assets.paths << root.join('vendor/javascript')
19
+ app.config.assets.paths << root.join('vendor/stylesheets')
20
+ app.config.assets.precompile += %w[spree_google_products_manifest]
21
+ end
22
+
23
+ initializer 'spree_google_products.importmap', before: 'importmap' do |app|
24
+ app.config.importmap.paths << root.join('config/importmap.rb')
25
+ # https://github.com/rails/importmap-rails?tab=readme-ov-file#sweeping-the-cache-in-development-and-test
26
+ app.config.importmap.cache_sweepers << root.join('app/javascript')
27
+ end
28
+
29
+ def self.activate
30
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
31
+ Rails.configuration.cache_classes ? require(c) : load(c)
32
+ end
33
+ end
34
+
35
+ config.to_prepare(&method(:activate).to_proc)
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ # Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
3
+ #
4
+ # Example adding this to your spec_helper will load these Factories for use:
5
+ # require 'spree_google_products/factories'
6
+ end
@@ -0,0 +1,7 @@
1
+ module SpreeGoogleProducts
2
+ VERSION = '1.0'.freeze
3
+
4
+ def gem_version
5
+ Gem::Version.new(VERSION)
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'spree_core'
2
+ require 'spree_extension'
3
+ require 'spree_google_products/engine'
4
+ require 'spree_google_products/version'
5
+ require 'spree_google_products/configuration'
6
+
7
+ module SpreeGoogleProducts
8
+ mattr_accessor :queue
9
+
10
+ def self.queue
11
+ @@queue ||= (defined?(Spree.queues) ? Spree.queues.default : :default)
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ namespace :spree_google_shopping do
2
+ desc "Download and import official Google Taxonomy"
3
+ task seed_taxons: :environment do
4
+ require 'open-uri'
5
+
6
+ url = "https://www.google.com/basepages/producttype/taxonomy-with-ids.en-US.txt"
7
+ puts "⬇️ Downloading Google Taxonomy from: #{url}..."
8
+
9
+ ActiveRecord::Base.transaction do
10
+ Spree::GoogleTaxon.delete_all
11
+
12
+ count = 0
13
+ data = []
14
+
15
+ URI.open(url) do |file|
16
+ file.each_line do |line|
17
+ next if line.start_with?('#') || line.strip.empty?
18
+
19
+ parts = line.strip.split(' - ', 2)
20
+
21
+ if parts.length == 2
22
+ data << {
23
+ google_id: parts[0],
24
+ name: parts[1]
25
+ }
26
+ count += 1
27
+ end
28
+
29
+ if data.size >= 1000
30
+ Spree::GoogleTaxon.insert_all(data)
31
+ print "."
32
+ data = []
33
+ end
34
+ end
35
+ end
36
+
37
+ Spree::GoogleTaxon.insert_all(data) if data.any?
38
+ puts "\n✅ Successfully seeded #{count} Google Taxons!"
39
+ end
40
+ end
41
+ end