shopify_toolkit 0.3.5 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 457f7f3d4a71f0d16b2626673522a0c39fff7bf0cd0d77108dfad5be5ebed8dc
4
- data.tar.gz: bbfc1ddfb371307e3eacfdcc24896628772886bb89e9333b5f0efab79dd34e42
3
+ metadata.gz: fbc1361f6cf3ce45a4b6d004f55cc98b4c9f748c2863e281874cc48f085e7b9d
4
+ data.tar.gz: 2e9a2fc920f7f7178f5f4ea3ccc945e6ac0c4fb1d50a426d77708888837a4941
5
5
  SHA512:
6
- metadata.gz: 2d81a0a15f9316488ffa6a7f56b5bbcab71196c8802568775d624802f9cba40ede8043d7e29e25008bc4e716bb01cdc5a299e4c542be92a53e1b681ef0c38b3e
7
- data.tar.gz: 1896abe675d312bd57352cae7ca5e19a217055b551ed1ae992f1e453985c31361ea2d515333b6a5f129dbab4aab18ce0bb716faae49fc01615e76e6597bb7242
6
+ metadata.gz: 44168636617828f3420d2fd0da7322c962f0b1a7a34e57cf9fe8642acc67d1dfe22dd5fabc934f0ba671e83eecf1623f47dd99f37c98821ef8689b81df91bc7f
7
+ data.tar.gz: 3fa125127d19a08be48ef8a2165628816653527f272fb0cd352962ff1f5bdb55d5c2a2d8ea08bf3eabd174278e4d9d5777fcee2988d0ee6956b52ff464686c8e
data/README.md CHANGED
@@ -11,9 +11,14 @@ A toolkit for working with Custom Shopify Apps built on Rails.
11
11
  ## Features/Roadmap
12
12
 
13
13
  - [x] Shopify/Matrixify CSV tools
14
- - [ ] Metafield/Metaobject migrations (just like ActiveRecord migrations, but for Shopify!)
15
- - [x] Metafield Definitions management API
16
- - [ ] Metaobject Definitions management API
14
+ - [x] Metafield/Metaobject migrations (just like ActiveRecord migrations, but for Shopify!)
15
+ - [x] Metafield Definitions management API
16
+ - [x] Metaobject Definitions management API
17
+ - [x] Create
18
+ - [x] Update
19
+ - [x] Find
20
+ - [ ] Delete
21
+ - [ ] Metaobject Instances management API
17
22
  - [ ] GraphQL Admin API code generation (syntax checking, etc)
18
23
  - [ ] GraphQL Admin API client with built-in rate limiting
19
24
  - [ ] GraphQL Admin API client with built-in caching
@@ -30,38 +35,51 @@ bundle add shopify_toolkit
30
35
 
31
36
  ## Usage
32
37
 
33
- ### Migrating Metafields definitions using ActiveRecord Migrations
38
+ ### Migrating Metafields and Metaobjects
34
39
 
35
40
  Within a Rails application created with ShopifyApp, generate a new migration file:
36
41
 
37
42
  ```bash
38
- rails generate migration AddMetafieldDefinitions
43
+ touch config/shopify/migrate/$(date +%Y%m%d%H%M%S)_add_product_press_releases.rb
39
44
  ```
40
45
 
41
- Include the `ShopifyToolkit::MetafieldStatements` module in your migration file
42
- in order to use the metafield statements:
43
-
46
+ Then, add the following code to the migration file:
44
47
  ```ruby
45
- class AddMetafieldDefinitions < ActiveRecord::Migration[7.0]
46
- include ShopifyToolkit::MetafieldStatements
47
-
48
+ # config/shopify/migrate/20250528130134_add_product_press_releases.rb
49
+ class AddProductPressReleases < ShopifyToolkit::Migration
48
50
  def up
49
- Shop.first!.with_shopify_session do
50
- create_metafield :products, :my_metafield, :single_line_text_field, name: "My Metafield"
51
- end
51
+ create_metaobject_definition :press_release,
52
+ name: "Press Release",
53
+ displayNameKey: "name",
54
+ access: { storefront: "PUBLIC_READ" },
55
+ capabilities: {
56
+ onlineStore: { enabled: false },
57
+ publishable: { enabled: true },
58
+ translatable: { enabled: true },
59
+ renderable: { enabled: false },
60
+ },
61
+ fieldDefinitions: [
62
+ { key: "name", name: "Title", required: true, type: "single_line_text_field" },
63
+ { key: "body", name: "Body", required: true, type: "multi_line_text_field" },
64
+ ]
65
+
66
+ metaobject_definition_id = get_metaobject_definition_gid :press_release
67
+
68
+ create_metafield :products, :press_release, :metaobject_reference, name: "Press Release", validations: [
69
+ { name: "metaobject_definition_id", value: metaobject_definition_id }
70
+ ]
52
71
  end
53
72
 
54
73
  def down
55
- Shop.first!.with_shopify_session do
56
- remove_metafield :products, :my_metafield
57
- end
74
+ # Noop. We don't want to remove the metaobject definition, since it might be populated with data.
58
75
  end
59
76
  end
60
77
  ```
61
- Then run the migration:
78
+
79
+ Then run the migrations:
62
80
 
63
81
  ```bash
64
- rails db:migrate
82
+ bundle exec shopify-toolkit migrate
65
83
  ```
66
84
 
67
85
  ### Creating a Metafield Schema Definition
data/exe/shopify-toolkit CHANGED
@@ -1,72 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'csv'
4
- require 'active_record'
5
- require 'thor'
6
- require 'tmpdir'
7
3
  require 'shopify_toolkit'
8
4
 
9
- class ShopifyToolkit::CommandLine < Thor
10
- RESERVED_COLUMN_NAMES = %w[select type id]
11
-
12
- class Result < ActiveRecord::Base
13
- def self.comments
14
- distinct.pluck(:import_comment)
15
- end
16
-
17
- def self.with_comment(text)
18
- where("import_comment LIKE ?", "%#{text}%")
19
- end
20
- end
21
-
22
- desc "analyze CSV_PATH", "Analyze results file at path CSV_PATH"
23
- method_option :force_import, type: :boolean, default: false
24
- method_option :tmp_dir, type: :string, default: Dir.tmpdir
25
- def analyze(csv_path)
26
- csv_path = File.expand_path(csv_path)
27
- underscore = ->(string) { string.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/(^_+|_+$)/, "") }
28
- csv = CSV.open(csv_path, liberal_parsing:true )
29
- header_to_column = -> { RESERVED_COLUMN_NAMES.include?(_1.to_s) ? "#{_1}_1" : _1 }
30
- headers = csv.shift.map(&underscore).map(&header_to_column)
31
- basename = File.basename csv_path
32
- database = "#{options[:tmp_dir]}/shopify-toolkit-analyze-#{underscore[basename]}.sqlite3"
33
- should_import = options[:force_import] || !File.exist?(database)
34
- to_record = ->(row) { headers.zip(row.each{ |c| c.delete!("\u0000") if String === c }).to_h.transform_keys(&header_to_column) }
35
-
36
- File.delete(database) if should_import && File.exist?(database)
37
-
38
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database:)
39
-
40
- if should_import
41
- puts "==> Importing #{csv_path} into #{database}"
42
- ActiveRecord::Schema.define do
43
- create_table :results, force: true do |t|
44
- t.json :data
45
- headers.each { |header| t.string header }
46
- end
47
- add_index :results, :import_result if headers.include?('import_result')
48
- end
49
- csv.each_slice(5000) { |rows| print "."; Result.insert_all(rows.map(&to_record)) }
50
- puts
51
- end
52
-
53
- puts "==> Starting console for #{basename}"
54
- require "irb"
55
- IRB.conf[:IRB_NAME] = basename
56
- Result.class_eval { binding.irb(show_code: false) }
57
- end
58
-
59
- desc "schema_load", 'Load schema from "config/shopify/schema.rb"'
60
- def schema_load
61
- require "./config/environment"
62
- ::Shop.sole.with_shopify_session { ShopifyToolkit::Schema.load! }
63
- end
64
-
65
- desc "schema_dump", 'Dump schema to "config/shopify/schema.rb"'
66
- def schema_dump
67
- require "./config/environment"
68
- ::Shop.sole.with_shopify_session { ShopifyToolkit::Schema.dump! }
69
- end
70
- end
71
-
72
5
  ShopifyToolkit::CommandLine.start(ARGV)
@@ -1,3 +1,5 @@
1
+ require "shopify_api"
2
+
1
3
  module ShopifyToolkit::AdminClient
2
4
  API_VERSION = "2024-10"
3
5
 
@@ -0,0 +1,89 @@
1
+ require 'csv'
2
+ require 'active_record'
3
+ require 'thor'
4
+ require 'tmpdir'
5
+
6
+ class ShopifyToolkit::CommandLine < Thor
7
+ RESERVED_COLUMN_NAMES = %w[select type id]
8
+
9
+ class Result < ActiveRecord::Base
10
+ def self.comments
11
+ distinct.pluck(:import_comment)
12
+ end
13
+
14
+ def self.with_comment(text)
15
+ where("import_comment LIKE ?", "%#{text}%")
16
+ end
17
+ end
18
+
19
+ desc "analyze CSV_PATH", "Analyze results file at path CSV_PATH"
20
+ method_option :force_import, type: :boolean, default: false
21
+ method_option :tmp_dir, type: :string, default: Dir.tmpdir
22
+ def analyze(csv_path)
23
+ csv_path = File.expand_path(csv_path)
24
+ underscore = ->(string) { string.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/(^_+|_+$)/, "") }
25
+ csv = CSV.open(csv_path, liberal_parsing:true )
26
+ header_to_column = -> { RESERVED_COLUMN_NAMES.include?(_1.to_s) ? "#{_1}_1" : _1 }
27
+ headers = csv.shift.map(&underscore).map(&header_to_column)
28
+ basename = File.basename csv_path
29
+ database = "#{options[:tmp_dir]}/shopify-toolkit-analyze-#{underscore[basename]}.sqlite3"
30
+ should_import = options[:force_import] || !File.exist?(database)
31
+ to_record = ->(row) { headers.zip(row.each{ |c| c.delete!("\u0000") if String === c }).to_h.transform_keys(&header_to_column) }
32
+
33
+ File.delete(database) if should_import && File.exist?(database)
34
+
35
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database:)
36
+
37
+ if should_import
38
+ puts "==> Importing #{csv_path} into #{database}"
39
+ ActiveRecord::Schema.define do
40
+ create_table :results, force: true do |t|
41
+ t.json :data
42
+ headers.each { |header| t.string header }
43
+ end
44
+ add_index :results, :import_result if headers.include?('import_result')
45
+ end
46
+ csv.each_slice(5000) { |rows| print "."; Result.insert_all(rows.map(&to_record)) }
47
+ puts
48
+ end
49
+
50
+ puts "==> Starting console for #{basename}"
51
+ require "irb"
52
+ IRB.conf[:IRB_NAME] = basename
53
+ Result.class_eval { binding.irb(show_code: false) }
54
+ end
55
+
56
+ desc "migrate", "Run migrations"
57
+ def migrate
58
+ require "./config/environment"
59
+ ::Shop.sole.with_shopify_session { ShopifyToolkit::Migrator.new.up }
60
+ end
61
+
62
+ desc "rollback", "Rollback last migration"
63
+ def rollback
64
+ require "./config/environment"
65
+ ::Shop.sole.with_shopify_session { ShopifyToolkit::Migrator.new.down }
66
+ end
67
+
68
+ desc "redo", "Run migrations down and up again"
69
+ def redo
70
+ require "./config/environment"
71
+ ::Shop.sole.with_shopify_session { ShopifyToolkit::Migrator.new.redo }
72
+ end
73
+
74
+ desc "schema_load", 'Load schema from "config/shopify/schema.rb"'
75
+ def schema_load
76
+ require "./config/environment"
77
+ ::Shop.sole.with_shopify_session { ShopifyToolkit::Schema.load! }
78
+ end
79
+
80
+ desc "schema_dump", 'Dump schema to "config/shopify/schema.rb"'
81
+ def schema_dump
82
+ require "./config/environment"
83
+ ::Shop.sole.with_shopify_session { ShopifyToolkit::Schema.dump! }
84
+ end
85
+
86
+ def self.exit_on_failure?
87
+ true
88
+ end
89
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shopify_toolkit/migration/logging"
4
- require "shopify_toolkit/admin_client"
5
3
  require "active_support/concern"
6
4
 
7
5
  module ShopifyToolkit::MetafieldStatements
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module ShopifyToolkit::MetaobjectStatements
6
+ extend ActiveSupport::Concern
7
+ include ShopifyToolkit::Migration::Logging
8
+ include ShopifyToolkit::AdminClient
9
+
10
+ def self.log_time(method_name)
11
+ current_method = instance_method(method_name)
12
+ define_method(method_name) do |*args, **kwargs, &block|
13
+ say_with_time("#{method_name}(#{args.map(&:inspect).join(', ')})") { current_method.bind(self).call(*args, **kwargs, &block) }
14
+ end
15
+ end
16
+
17
+ # create_metafield :products, :my_metafield, :single_line_text_field, name: "Prova"
18
+ # @param namespace: if nil the metafield will be app-specific (default: :custom)
19
+ log_time \
20
+ def create_metaobject_definition(type, **options)
21
+ # Skip creation if metafield already exists
22
+ existing_gid = get_metaobject_definition_gid(type)
23
+ if existing_gid
24
+ say "Metaobject #{type} already exists, skipping creation"
25
+ return existing_gid
26
+ end
27
+
28
+ # https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/metafieldDefinitionCreate
29
+ query =
30
+ "# GraphQL
31
+ mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {
32
+ metaobjectDefinitionCreate(definition: $definition) {
33
+ metaobjectDefinition {
34
+ id
35
+ name
36
+ type
37
+ fieldDefinitions {
38
+ name
39
+ key
40
+ }
41
+ }
42
+ userErrors {
43
+ field
44
+ message
45
+ code
46
+ }
47
+ }
48
+ }
49
+ "
50
+ variables = { definition: { type:, **options } }
51
+
52
+ shopify_admin_client
53
+ .query(query:, variables:)
54
+ .tap { handle_shopify_admin_client_errors(_1, "data.metaobjectDefinitionCreate.userErrors") }
55
+ end
56
+
57
+ def get_metaobject_definition_gid(type)
58
+ result =
59
+ shopify_admin_client
60
+ .query(
61
+ query:
62
+ "# GraphQL
63
+ query GetMetaobjectDefinitionID($type: String!) {
64
+ metaobjectDefinitionByType(type: $type) {
65
+ id
66
+ }
67
+ }",
68
+ variables: { type: type.to_s },
69
+ )
70
+ .tap { handle_shopify_admin_client_errors(_1) }
71
+ .body
72
+
73
+ result.dig("data", "metaobjectDefinitionByType", "id")
74
+ end
75
+
76
+ def update_metaobject_definition(type, **options)
77
+ existing_gid = get_metaobject_definition_gid(type)
78
+
79
+ raise "Metaobject #{type} does not exist" unless existing_gid
80
+
81
+ # https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/metaobjectDefinitionUpdate
82
+ query =
83
+ "# GraphQL
84
+ mutation UpdateMetaobjectDefinition($id: ID!, $definition: MetaobjectDefinitionUpdateInput!) {
85
+ metaobjectDefinitionUpdate(id: $id, definition: $definition) {
86
+ metaobjectDefinition {
87
+ id
88
+ name
89
+ type
90
+ fieldDefinitions {
91
+ name
92
+ key
93
+ }
94
+ }
95
+ userErrors {
96
+ field
97
+ message
98
+ code
99
+ }
100
+ }
101
+ }
102
+ "
103
+ variables = { id: existing_gid, definition: { **options } }
104
+
105
+ shopify_admin_client
106
+ .query(query:, variables:)
107
+ .tap { handle_shopify_admin_client_errors(_1, "data.metaobjectDefinitionUpdate.userErrors") }
108
+ end
109
+
110
+ def self.define(&block)
111
+ context = Object.new
112
+ context.extend(self)
113
+
114
+ context.instance_eval(&block) if block_given?(&block)
115
+ context
116
+ end
117
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/benchmark"
4
+
5
+ class ShopifyToolkit::Migration
6
+ include ShopifyToolkit::AdminClient
7
+ include ShopifyToolkit::MetafieldStatements
8
+ include ShopifyToolkit::MetaobjectStatements
9
+ include ShopifyToolkit::Migration::Logging
10
+
11
+ class IrreversibleMigration < StandardError
12
+ end
13
+
14
+ def logger
15
+ @logger ||= ActiveSupport::Logger.new(STDOUT).tap do |logger|
16
+ logger.formatter = proc do |severity, datetime, progname, msg|
17
+ "#{severity}: #{msg}\n"
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.[](api_version)
23
+ klass = Class.new(self)
24
+ klass.const_set(:API_VERSION, api_version)
25
+ klass
26
+ end
27
+
28
+ attr_reader :name, :version
29
+
30
+ def initialize(name, version)
31
+ @name = name
32
+ @version = version
33
+ end
34
+
35
+ def announce(message)
36
+ super "#{version} #{name}: #{message}"
37
+ end
38
+
39
+ def migrate(direction)
40
+ case direction
41
+ when :up
42
+ announce("migrating")
43
+ time_elapsed = ActiveSupport::Benchmark.realtime { up }
44
+ announce("migrated (%.4fs)" % time_elapsed)
45
+
46
+ when :down
47
+ announce("reverting")
48
+ time_elapsed = ActiveSupport::Benchmark.realtime { down }
49
+ announce("reverted (%.4fs)" % time_elapsed)
50
+
51
+ else
52
+ raise ArgumentError, "Unknown migration direction: #{direction}"
53
+ end
54
+ end
55
+
56
+ def up
57
+ # Implement in subclass
58
+ end
59
+
60
+ def down
61
+ raise IrreversibleMigration
62
+ end
63
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/benchmarkable"
4
+ require "active_support/core_ext/module/delegation"
5
+
6
+ class ShopifyToolkit::Migrator # :nodoc:
7
+ include ShopifyToolkit::AdminClient
8
+ include ShopifyToolkit::MetafieldStatements
9
+
10
+ singleton_class.attr_accessor :migrations_paths
11
+ self.migrations_paths = ["config/shopify/migrate"]
12
+
13
+ attr_reader :migrated_versions, :migrations, :migrations_paths
14
+
15
+ def initialize(migrations_paths: self.class.migrations_paths)
16
+ @migrations_paths = migrations_paths
17
+ @migrated_versions = read_or_create_metafield["migrated_versions"]
18
+ @migrations = load_migrations # [MigrationProxy<Migration1>, MigrationProxy<Migration2>, ...]
19
+ end
20
+
21
+ def current_version
22
+ migrated.max || 0
23
+ end
24
+
25
+ def up
26
+ pending_migrations = migrations.reject { migrated_versions.include?(_1.version) }
27
+
28
+ pending_migrations.each do |migration|
29
+ migration.migrate(:up)
30
+ migrated_versions << migration.version
31
+ end
32
+ ensure
33
+ update_metafield
34
+ end
35
+
36
+ def executed_migrations
37
+ migrations.select { migrated_versions.include?(_1.version) }
38
+ end
39
+
40
+ def down
41
+ # For now we'll just rollback the last one
42
+ executed_migrations.last(1).each do |migration|
43
+ migration.migrate(:down)
44
+ migrated_versions.delete(migration.version)
45
+ end
46
+ ensure
47
+ update_metafield
48
+ end
49
+
50
+ def query(query, **variables)
51
+ shopify_admin_client
52
+ .query(query:, variables:)
53
+ .tap { handle_shopify_admin_client_errors(_1) }
54
+ .body
55
+ .dig("data")
56
+ end
57
+
58
+ def update_metafield
59
+ namespace = :shopify_toolkit
60
+ key = :migrations
61
+
62
+ value = { "migrated_versions" => migrated_versions.to_a.sort }.to_json
63
+
64
+ owner_id = query(%(query GetShopGId { shop { id } })).dig("shop", "id")
65
+
66
+ query = <<~GRAPHQL
67
+ mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
68
+ metafieldsSet(metafields: $metafields) {
69
+ userErrors {
70
+ field
71
+ message
72
+ }
73
+ }
74
+ }
75
+ GRAPHQL
76
+ query(query, metafields: [{ namespace:, key:, ownerId: owner_id, value: }])
77
+
78
+ nil
79
+ end
80
+
81
+ def read_or_create_metafield
82
+ namespace = :shopify_toolkit
83
+ key = :migrations
84
+
85
+ value = query(
86
+ "query ShopMetafield($namespace: String!, $key: String!) {shop {metafield(namespace: $namespace, key: $key) {value}}}",
87
+ namespace:, key:,
88
+ ).dig("shop", "metafield", "value")
89
+
90
+ if value.nil?
91
+ create_metafield :shop, key, :json, namespace:, name: "Migrations metadata"
92
+ return { "migrated_versions" => [] }
93
+ end
94
+
95
+ JSON.parse(value)
96
+ end
97
+
98
+ def redo
99
+ down
100
+ up
101
+ end
102
+
103
+ def migration_files
104
+ paths = Array(migrations_paths)
105
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
106
+ end
107
+
108
+ def parse_migration_filename(filename)
109
+ File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
110
+ end
111
+
112
+ def load_migrations
113
+ migrations = migration_files.map do |file|
114
+ version, name, scope = parse_migration_filename(file)
115
+ raise "missing version #{file}" unless version
116
+ raise "missing name #{file}" unless name
117
+ version = version.to_i
118
+ name = name.camelize
119
+
120
+ MigrationProxy.new(name, version, file, scope)
121
+ end
122
+
123
+ migrations.sort_by(&:version)
124
+ end
125
+
126
+ # MigrationProxy is used to defer loading of the actual migration classes
127
+ # until they are needed
128
+ MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
129
+ def initialize(name, version, filename, scope)
130
+ super
131
+ @migration = nil
132
+ end
133
+
134
+ def basename
135
+ File.basename(filename)
136
+ end
137
+
138
+ delegate :migrate, :up, :down, :announce, :say, :say_with_time, to: :migration
139
+
140
+ private
141
+ def migration
142
+ @migration ||= load_migration
143
+ end
144
+
145
+ def load_migration
146
+ Object.send(:remove_const, name) rescue nil
147
+
148
+ load(File.expand_path(filename))
149
+ name.constantize.new(name, version)
150
+ end
151
+ end
152
+ end
@@ -3,6 +3,7 @@
3
3
  require "stringio"
4
4
  require "shopify_api"
5
5
  require "rails"
6
+ require "active_support/core_ext/module/delegation"
6
7
 
7
8
  module ShopifyToolkit::Schema
8
9
  extend self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyToolkit
4
- VERSION = "0.3.5"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -4,7 +4,6 @@ require_relative "shopify_toolkit/version"
4
4
  require "zeitwerk"
5
5
 
6
6
  module ShopifyToolkit
7
-
8
7
  def self.loader
9
8
  @loader ||= Zeitwerk::Loader.for_gem
10
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_toolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elia Schito
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-04-24 00:00:00.000000000 Z
12
+ date: 2025-06-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -111,8 +111,12 @@ files:
111
111
  - exe/shopify-toolkit
112
112
  - lib/shopify_toolkit.rb
113
113
  - lib/shopify_toolkit/admin_client.rb
114
+ - lib/shopify_toolkit/command_line.rb
114
115
  - lib/shopify_toolkit/metafield_statements.rb
116
+ - lib/shopify_toolkit/metaobject_statements.rb
117
+ - lib/shopify_toolkit/migration.rb
115
118
  - lib/shopify_toolkit/migration/logging.rb
119
+ - lib/shopify_toolkit/migrator.rb
116
120
  - lib/shopify_toolkit/schema.rb
117
121
  - lib/shopify_toolkit/version.rb
118
122
  - sig/shopify_toolkit.rbs