shopify_toolkit 0.5.1 → 0.5.2
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 +4 -4
- data/README.md +44 -3
- data/lib/shopify_toolkit/metafield_statements.rb +21 -0
- data/lib/shopify_toolkit/metaobject_statements.rb +142 -24
- data/lib/shopify_toolkit/metaobject_utilities.rb +107 -0
- data/lib/shopify_toolkit/migrator.rb +1 -0
- data/lib/shopify_toolkit/schema.rb +223 -11
- data/lib/shopify_toolkit/version.rb +1 -1
- metadata +8 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 767c783d8c115219c9b25c83e6defa01772cc321a411c072e149ed82e10bc2ba
         | 
| 4 | 
            +
              data.tar.gz: 576dfd3753579fac15388f5b22186a5b35b9298d974875ace256532b0af2063d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e441dfcdffb2552f0773d132db88a8204a0f60c2624f40f8cff2b0d01304de480e92a95d8d6db5f396bec0c1bce7179d1315fb49b3bb0347f59c14070d4b1685
         | 
| 7 | 
            +
              data.tar.gz: 14786f1aa5d5d4e27274f591996f90960c6fdd326f1613088fb54f52f26ebf447b06eeaea4ebf3455c0f868bd9d5f1e71e176c58b75d15c499dd0dd9186caf2f
         | 
    
        data/README.md
    CHANGED
    
    | @@ -24,6 +24,10 @@ A toolkit for working with Custom Shopify Apps built on Rails. | |
| 24 24 | 
             
            - [ ] GraphQL Admin API client with built-in caching
         | 
| 25 25 | 
             
            - [ ] GraphQL Admin API client with built-in error handling
         | 
| 26 26 | 
             
            - [ ] GraphQL Admin API client with built-in logging
         | 
| 27 | 
            +
            - [ ] Bulk Operations
         | 
| 28 | 
            +
              - [ ] Interface for uploading and getting results for query / mutation
         | 
| 29 | 
            +
              - [ ] Error handling and Logging
         | 
| 30 | 
            +
              - [ ] Callbacks
         | 
| 27 31 |  | 
| 28 32 | 
             
            ## Installation
         | 
| 29 33 |  | 
| @@ -40,10 +44,11 @@ bundle add shopify_toolkit | |
| 40 44 | 
             
            Within a Rails application created with ShopifyApp, generate a new migration file:
         | 
| 41 45 |  | 
| 42 46 | 
             
            ```bash
         | 
| 43 | 
            -
            bundle exec shopify-toolkit  | 
| 47 | 
            +
            bundle exec shopify-toolkit generate_migration AddProductPressReleases
         | 
| 44 48 | 
             
            ```
         | 
| 45 49 |  | 
| 46 50 | 
             
            Then, add the following code to the migration file:
         | 
| 51 | 
            +
             | 
| 47 52 | 
             
            ```ruby
         | 
| 48 53 | 
             
            # config/shopify/migrate/20250528130134_add_product_press_releases.rb
         | 
| 49 54 | 
             
            class AddProductPressReleases < ShopifyToolkit::Migration
         | 
| @@ -96,6 +101,40 @@ ShopifyToolkit::MetafieldSchema.define do | |
| 96 101 | 
             
            end
         | 
| 97 102 | 
             
            ```
         | 
| 98 103 |  | 
| 104 | 
            +
            ### Monkey Patching for Specific Use Cases
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            In some scenarios, you may need to customize the behavior of ShopifyToolkit to handle specific limitations or requirements. For example, you might want to skip certain Shopify-managed metaobject definitions that cannot be created due to reserved namespaces or limited access permissions.
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            ```rb
         | 
| 109 | 
            +
            # config/initializers/shopify_toolkit_patches.rb
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            require "shopify_toolkit/metaobject_statements"
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            module ShopifyToolkit
         | 
| 114 | 
            +
              module MetaobjectStatements
         | 
| 115 | 
            +
                # Override create_metaobject_definition to skip Shopify-managed definitions
         | 
| 116 | 
            +
                alias_method :original_create_metaobject_definition, :create_metaobject_definition
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def create_metaobject_definition(type, **options)
         | 
| 119 | 
            +
                  # Skip Shopify-managed metaobject definitions during schema load
         | 
| 120 | 
            +
                  if type.to_s.start_with?("shopify--")
         | 
| 121 | 
            +
                    say "Skipping Shopify-managed metaobject definition: #{type}"
         | 
| 122 | 
            +
                    return
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                  original_create_metaobject_definition(type, **options)
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
            end
         | 
| 129 | 
            +
            ```
         | 
| 130 | 
            +
             | 
| 131 | 
            +
            This approach allows you to:
         | 
| 132 | 
            +
             | 
| 133 | 
            +
            - Skip creation of reserved metaobject types (e.g., those prefixed with `shopify--`)
         | 
| 134 | 
            +
            - Handle cases where certain metaobjects cannot be created due to API limitations
         | 
| 135 | 
            +
            - Customize validation logic for specific metaobject types
         | 
| 136 | 
            +
            - Filter out problematic metafield references during schema loading
         | 
| 137 | 
            +
             | 
| 99 138 | 
             
            ### Analyzing a Matrixify CSV Result files
         | 
| 100 139 |  | 
| 101 140 | 
             
            Matrixify is a popular Shopify app that allows you to import/export data from Shopify using CSV files. The CSV files that Matrixify generates are very verbose and can be difficult to work with. This tool allows you to analyze the CSV files and extract the data you need.
         | 
| @@ -104,8 +143,10 @@ The tool will import the file into a local SQLite database | |
| 104 143 | 
             
            and open a console for you to run queries against the data
         | 
| 105 144 | 
             
            using ActiveRecord.
         | 
| 106 145 |  | 
| 146 | 
            +
            > Note: To use it with `bundle exec`, sqlite3 must be included in the project’s bundle
         | 
| 147 | 
            +
             | 
| 107 148 | 
             
            ```shell
         | 
| 108 | 
            -
            shopify- | 
| 149 | 
            +
            shopify-toolkit analyze products-result.csv --force-import
         | 
| 109 150 | 
             
            ==> Importing products-result.csv into /var/folders/hn/z7b7s1kj3js4k7_qk3lj27kr0000gn/T/shopify-toolkit-analyze-products_result_csv.sqlite3
         | 
| 110 151 | 
             
            -- create_table(:results, {:force=>true})
         | 
| 111 152 | 
             
               -> 0.0181s
         | 
| @@ -119,7 +160,7 @@ shopify-csv analyze products-result.csv --force-import | |
| 119 160 | 
             
             "UPDATE: Found by Handle | Assuming MERGE for Variant Command | Assuming MERGE for Tags Command | Variants updated by SKU: 1 | Warning: The following media were not uploaded to Shopify: [https://mymedia.com/image.jpg: Error downloading from Web: 302 Moved Temporarily]",
         | 
| 120 161 | 
             
             ...]
         | 
| 121 162 | 
             
            >> first
         | 
| 122 | 
            -
            #< | 
| 163 | 
            +
            #<ShopifyToolkit::Result:0x0000000300bf1668
         | 
| 123 164 | 
             
             id: 1,
         | 
| 124 165 | 
             
             data: nil,
         | 
| 125 166 | 
             
             handle: "my-product",
         | 
| @@ -6,6 +6,7 @@ module ShopifyToolkit::MetafieldStatements | |
| 6 6 | 
             
              extend ActiveSupport::Concern
         | 
| 7 7 | 
             
              include ShopifyToolkit::Migration::Logging
         | 
| 8 8 | 
             
              include ShopifyToolkit::AdminClient
         | 
| 9 | 
            +
              include ShopifyToolkit::MetaobjectUtilities
         | 
| 9 10 |  | 
| 10 11 | 
             
              def self.log_time(method_name)
         | 
| 11 12 | 
             
                current_method = instance_method(method_name)
         | 
| @@ -26,6 +27,21 @@ module ShopifyToolkit::MetafieldStatements | |
| 26 27 | 
             
                  return
         | 
| 27 28 | 
             
                end
         | 
| 28 29 |  | 
| 30 | 
            +
                # Process validations to convert metaobject types to GIDs (only for metaobject reference fields)
         | 
| 31 | 
            +
                if options[:validations] && is_metaobject_reference_type?(type)
         | 
| 32 | 
            +
                  begin
         | 
| 33 | 
            +
                    options[:validations] = convert_validations_types_to_gids(options[:validations])
         | 
| 34 | 
            +
                  rescue RuntimeError => e
         | 
| 35 | 
            +
                    if e.message.include?("not found")
         | 
| 36 | 
            +
                      say "ERROR: Cannot create metafield #{namespace}:#{key} - references non-existent metaobject. This suggests the metaobject was filtered out or failed to create."
         | 
| 37 | 
            +
                      say "       Original error: #{e.message}"
         | 
| 38 | 
            +
                      return
         | 
| 39 | 
            +
                    else
         | 
| 40 | 
            +
                      raise e
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 29 45 | 
             
                # https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/metafieldDefinitionCreate
         | 
| 30 46 | 
             
                query =
         | 
| 31 47 | 
             
                  "# GraphQL
         | 
| @@ -117,6 +133,11 @@ module ShopifyToolkit::MetafieldStatements | |
| 117 133 | 
             
                  return
         | 
| 118 134 | 
             
                end
         | 
| 119 135 |  | 
| 136 | 
            +
                # Process validations to convert metaobject types to GIDs (only for metaobject reference fields)
         | 
| 137 | 
            +
                if options[:validations] && options[:type] && is_metaobject_reference_type?(options[:type])
         | 
| 138 | 
            +
                  options[:validations] = convert_validations_types_to_gids(options[:validations])
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
             | 
| 120 141 | 
             
                shopify_admin_client
         | 
| 121 142 | 
             
                  .query(
         | 
| 122 143 | 
             
                    # Documentation: https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/metafieldDefinitionUpdate
         | 
| @@ -6,6 +6,9 @@ module ShopifyToolkit::MetaobjectStatements | |
| 6 6 | 
             
              extend ActiveSupport::Concern
         | 
| 7 7 | 
             
              include ShopifyToolkit::Migration::Logging
         | 
| 8 8 | 
             
              include ShopifyToolkit::AdminClient
         | 
| 9 | 
            +
              include ShopifyToolkit::MetaobjectUtilities
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              @@pending_field_validations = []
         | 
| 9 12 |  | 
| 10 13 | 
             
              def self.log_time(method_name)
         | 
| 11 14 | 
             
                current_method = instance_method(method_name)
         | 
| @@ -14,18 +17,152 @@ module ShopifyToolkit::MetaobjectStatements | |
| 14 17 | 
             
                end
         | 
| 15 18 | 
             
              end
         | 
| 16 19 |  | 
| 17 | 
            -
               | 
| 18 | 
            -
             | 
| 20 | 
            +
              def apply_pending_field_validations
         | 
| 21 | 
            +
                return if @@pending_field_validations.empty?
         | 
| 22 | 
            +
                
         | 
| 23 | 
            +
                say "Applying #{@@pending_field_validations.size} pending field validations"
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                @@pending_field_validations.reject! do |item|
         | 
| 26 | 
            +
                  metaobject_type = item[:metaobject_type]
         | 
| 27 | 
            +
                  field_key = item[:field_key]
         | 
| 28 | 
            +
                  validations = item[:validations]
         | 
| 29 | 
            +
                  
         | 
| 30 | 
            +
                  begin
         | 
| 31 | 
            +
                    success = add_field_validations_to_metaobject(metaobject_type, field_key, validations, item)
         | 
| 32 | 
            +
                    unless success
         | 
| 33 | 
            +
                      say "-- Deferring field '#{field_key}' in '#{metaobject_type}' (missing dependencies)"
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                    success
         | 
| 36 | 
            +
                  rescue StandardError => e
         | 
| 37 | 
            +
                    say "-- Failed to process field '#{field_key}' in '#{metaobject_type}': #{e.message}"
         | 
| 38 | 
            +
                    true # Remove from array (don't retry errors)
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 19 43 | 
             
              log_time \
         | 
| 20 44 | 
             
              def create_metaobject_definition(type, **options)
         | 
| 21 | 
            -
                # Skip creation if  | 
| 45 | 
            +
                # Skip creation if metaobject already exists
         | 
| 22 46 | 
             
                existing_gid = get_metaobject_definition_gid(type)
         | 
| 23 47 | 
             
                if existing_gid
         | 
| 24 48 | 
             
                  say "Metaobject #{type} already exists, skipping creation"
         | 
| 25 49 | 
             
                  return existing_gid
         | 
| 26 50 | 
             
                end
         | 
| 27 51 |  | 
| 28 | 
            -
                #  | 
| 52 | 
            +
                # Transform options for GraphQL API
         | 
| 53 | 
            +
                definition = options.merge(type: type.to_s)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                begin
         | 
| 56 | 
            +
                  # Convert field_definitions to fieldDefinitions and transform field structure
         | 
| 57 | 
            +
                  if options[:field_definitions]
         | 
| 58 | 
            +
                    definition[:fieldDefinitions] = options[:field_definitions].filter_map do |field|
         | 
| 59 | 
            +
                      field_def = build_field_definition(field)
         | 
| 60 | 
            +
                      field_needs_validations = is_metaobject_reference_type?(field[:type])
         | 
| 61 | 
            +
                      
         | 
| 62 | 
            +
                      if field[:validations]
         | 
| 63 | 
            +
                        begin
         | 
| 64 | 
            +
                          converted_validations = convert_validations_types_to_gids(field[:validations])
         | 
| 65 | 
            +
                          field_def[:validations] = converted_validations if converted_validations&.any?
         | 
| 66 | 
            +
                        rescue RuntimeError => e
         | 
| 67 | 
            +
                          if e.message.include?("not found")
         | 
| 68 | 
            +
                            @@pending_field_validations << {
         | 
| 69 | 
            +
                              metaobject_type: type,
         | 
| 70 | 
            +
                              field_key: field[:key],
         | 
| 71 | 
            +
                              field_definition: field,
         | 
| 72 | 
            +
                              validations: field[:validations]
         | 
| 73 | 
            +
                            }
         | 
| 74 | 
            +
                            say "Deferring field '#{field[:key]}' in '#{type}' (missing dependency)"
         | 
| 75 | 
            +
                            
         | 
| 76 | 
            +
                            if field_needs_validations
         | 
| 77 | 
            +
                              next # Skip this field entirely
         | 
| 78 | 
            +
                            end
         | 
| 79 | 
            +
                          end
         | 
| 80 | 
            +
                        end
         | 
| 81 | 
            +
                      elsif field_needs_validations
         | 
| 82 | 
            +
                        next # Skip fields that need validations but don't have any
         | 
| 83 | 
            +
                      end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      field_def
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                    
         | 
| 88 | 
            +
                    definition.delete(:field_definitions)
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  # Remove admin access to avoid API restrictions
         | 
| 92 | 
            +
                  if definition[:access]&.is_a?(Hash)
         | 
| 93 | 
            +
                    definition[:access] = definition[:access].dup
         | 
| 94 | 
            +
                    definition[:access].delete("admin") if definition[:access]["admin"]
         | 
| 95 | 
            +
                    definition.delete(:access) if definition[:access].empty?
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  # Clean up empty validations arrays that cause API errors
         | 
| 99 | 
            +
                  if definition[:fieldDefinitions]
         | 
| 100 | 
            +
                    definition[:fieldDefinitions].each do |field_def|
         | 
| 101 | 
            +
                      field_def.delete(:validations) if field_def[:validations]&.empty?
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
                  
         | 
| 105 | 
            +
                  result = create_metaobject_definition_immediate(definition)
         | 
| 106 | 
            +
                  result
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
              def add_field_validations_to_metaobject(metaobject_type, field_key, validations, item = nil)
         | 
| 111 | 
            +
                # Get the existing metaobject definition
         | 
| 112 | 
            +
                existing_gid = get_metaobject_definition_gid(metaobject_type)
         | 
| 113 | 
            +
                unless existing_gid
         | 
| 114 | 
            +
                  say "Error: Cannot add validations to '#{metaobject_type}' - metaobject not found"
         | 
| 115 | 
            +
                  return false
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                begin
         | 
| 119 | 
            +
                  converted_validations = convert_validations_types_to_gids(validations)
         | 
| 120 | 
            +
                  
         | 
| 121 | 
            +
                  if converted_validations&.any?
         | 
| 122 | 
            +
                    # Use the passed item, or try to find it (for backward compatibility)
         | 
| 123 | 
            +
                    if item.nil?
         | 
| 124 | 
            +
                      item = @@pending_field_validations.find { |pending_item| 
         | 
| 125 | 
            +
                        pending_item[:metaobject_type] == metaobject_type && pending_item[:field_key] == field_key 
         | 
| 126 | 
            +
                      }
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                    
         | 
| 129 | 
            +
                    if item && item[:field_definition]
         | 
| 130 | 
            +
                      field_def = item[:field_definition]
         | 
| 131 | 
            +
                      new_field = build_field_definition(field_def, converted_validations)
         | 
| 132 | 
            +
                      
         | 
| 133 | 
            +
                      field_operation = { create: new_field }
         | 
| 134 | 
            +
                      update_metaobject_definition(metaobject_type, fieldDefinitions: [field_operation])
         | 
| 135 | 
            +
                      
         | 
| 136 | 
            +
                      say "Added field '#{field_key}' to '#{metaobject_type}'"
         | 
| 137 | 
            +
                      return true
         | 
| 138 | 
            +
                    else
         | 
| 139 | 
            +
                      return false
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                  else
         | 
| 142 | 
            +
                    return false
         | 
| 143 | 
            +
                  end
         | 
| 144 | 
            +
                  
         | 
| 145 | 
            +
                rescue RuntimeError
         | 
| 146 | 
            +
                  return false # Keep trying later or don't retry errors
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
              end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
              private
         | 
| 151 | 
            +
             | 
| 152 | 
            +
              def build_field_definition(field, validations = nil)
         | 
| 153 | 
            +
                field_def = {
         | 
| 154 | 
            +
                  key: field[:key].to_s,
         | 
| 155 | 
            +
                  name: field[:name],
         | 
| 156 | 
            +
                  type: field[:type].to_s
         | 
| 157 | 
            +
                }
         | 
| 158 | 
            +
                field_def[:description] = field[:description] if field[:description]
         | 
| 159 | 
            +
                field_def[:required] = field[:required] if field[:required]
         | 
| 160 | 
            +
                field_def[:validations] = validations if validations&.any?
         | 
| 161 | 
            +
                field_def
         | 
| 162 | 
            +
              end
         | 
| 163 | 
            +
             | 
| 164 | 
            +
              def create_metaobject_definition_immediate(definition)
         | 
| 165 | 
            +
                # https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/metaobjectDefinitionCreate
         | 
| 29 166 | 
             
                query =
         | 
| 30 167 | 
             
                  "# GraphQL
         | 
| 31 168 | 
             
                  mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {
         | 
| @@ -47,32 +184,13 @@ module ShopifyToolkit::MetaobjectStatements | |
| 47 184 | 
             
                    }
         | 
| 48 185 | 
             
                  }
         | 
| 49 186 | 
             
                  "
         | 
| 50 | 
            -
                variables = { definition:  | 
| 187 | 
            +
                variables = { definition: definition }
         | 
| 51 188 |  | 
| 52 189 | 
             
                shopify_admin_client
         | 
| 53 190 | 
             
                  .query(query:, variables:)
         | 
| 54 191 | 
             
                  .tap { handle_shopify_admin_client_errors(_1, "data.metaobjectDefinitionCreate.userErrors") }
         | 
| 55 192 | 
             
              end
         | 
| 56 193 |  | 
| 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 194 | 
             
              def update_metaobject_definition(type, **options)
         | 
| 77 195 | 
             
                existing_gid = get_metaobject_definition_gid(type)
         | 
| 78 196 |  | 
| @@ -0,0 +1,107 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "active_support/concern"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module ShopifyToolkit::MetaobjectUtilities
         | 
| 6 | 
            +
              extend ActiveSupport::Concern
         | 
| 7 | 
            +
              include ShopifyToolkit::AdminClient
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def is_metaobject_reference_type?(type)
         | 
| 10 | 
            +
                type.to_sym.in?([:metaobject_reference, :"list.metaobject_reference", :mixed_reference, :"list.mixed_reference"])
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def convert_validations_types_to_gids(validations)
         | 
| 14 | 
            +
                return validations unless validations&.any?
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                validations.filter_map do |validation|
         | 
| 17 | 
            +
                  convert_metaobject_validation(validation)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def get_metaobject_definition_gid(type)
         | 
| 22 | 
            +
                result =
         | 
| 23 | 
            +
                  shopify_admin_client
         | 
| 24 | 
            +
                    .query(
         | 
| 25 | 
            +
                      query:
         | 
| 26 | 
            +
                        "# GraphQL
         | 
| 27 | 
            +
                          query GetMetaobjectDefinitionID($type: String!) {
         | 
| 28 | 
            +
                            metaobjectDefinitionByType(type: $type) {
         | 
| 29 | 
            +
                              id
         | 
| 30 | 
            +
                            }
         | 
| 31 | 
            +
                          }",
         | 
| 32 | 
            +
                      variables: { type: type.to_s },
         | 
| 33 | 
            +
                    )
         | 
| 34 | 
            +
                    .tap { handle_shopify_admin_client_errors(_1) }
         | 
| 35 | 
            +
                    .body
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                gid = result.dig("data", "metaobjectDefinitionByType", "id")
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                return gid
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def get_metaobject_definition_type_by_gid(gid)
         | 
| 43 | 
            +
                result =
         | 
| 44 | 
            +
                  shopify_admin_client
         | 
| 45 | 
            +
                    .query(
         | 
| 46 | 
            +
                      query:
         | 
| 47 | 
            +
                        "# GraphQL
         | 
| 48 | 
            +
                          query GetMetaobjectDefinitionType($id: ID!) {
         | 
| 49 | 
            +
                            metaobjectDefinition(id: $id) {
         | 
| 50 | 
            +
                              type
         | 
| 51 | 
            +
                            }
         | 
| 52 | 
            +
                          }",
         | 
| 53 | 
            +
                      variables: { id: gid },
         | 
| 54 | 
            +
                    )
         | 
| 55 | 
            +
                    .tap { handle_shopify_admin_client_errors(_1) }
         | 
| 56 | 
            +
                    .body
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                result.dig("data", "metaobjectDefinition", "type")
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              private
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def convert_metaobject_validation(validation)
         | 
| 64 | 
            +
                name = validation[:name] || validation["name"]
         | 
| 65 | 
            +
                value = validation[:value] || validation["value"]
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                return validation unless metaobject_type_validation?(name) && value
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                parsed_value = parse_json_if_needed(value)
         | 
| 70 | 
            +
                convert_types_to_gids(parsed_value)
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              def metaobject_type_validation?(name)
         | 
| 74 | 
            +
                name.in?(["metaobject_definition_type", "metaobject_definition_types"])
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def parse_json_if_needed(value)
         | 
| 78 | 
            +
                return value unless value.is_a?(String) && value.start_with?("[") && value.end_with?("]")
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                JSON.parse(value)
         | 
| 81 | 
            +
              rescue JSON::ParserError
         | 
| 82 | 
            +
                value
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              def convert_types_to_gids(value)
         | 
| 86 | 
            +
                if value.is_a?(Array)
         | 
| 87 | 
            +
                  convert_array_types_to_gids(value)
         | 
| 88 | 
            +
                else
         | 
| 89 | 
            +
                  convert_single_type_to_gid(value)
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def convert_array_types_to_gids(types)
         | 
| 94 | 
            +
                gids = types.map do |type|
         | 
| 95 | 
            +
                  gid = get_metaobject_definition_gid(type)
         | 
| 96 | 
            +
                  raise "Metaobject type '#{type}' not found" unless gid
         | 
| 97 | 
            +
                  gid
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
                { name: "metaobject_definition_ids", value: gids.to_json }
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              def convert_single_type_to_gid(type)
         | 
| 103 | 
            +
                gid = get_metaobject_definition_gid(type)
         | 
| 104 | 
            +
                raise "Metaobject type '#{type}' not found" unless gid
         | 
| 105 | 
            +
                { name: "metaobject_definition_id", value: gid }
         | 
| 106 | 
            +
              end
         | 
| 107 | 
            +
            end
         | 
| @@ -6,6 +6,7 @@ require "active_support/core_ext/module/delegation" | |
| 6 6 | 
             
            class ShopifyToolkit::Migrator # :nodoc:
         | 
| 7 7 | 
             
              include ShopifyToolkit::AdminClient
         | 
| 8 8 | 
             
              include ShopifyToolkit::MetafieldStatements
         | 
| 9 | 
            +
              include ShopifyToolkit::MetaobjectStatements
         | 
| 9 10 |  | 
| 10 11 | 
             
              singleton_class.attr_accessor :migrations_paths
         | 
| 11 12 | 
             
              self.migrations_paths = ["config/shopify/migrate"]
         | 
| @@ -8,6 +8,8 @@ require "active_support/core_ext/module/delegation" | |
| 8 8 | 
             
            module ShopifyToolkit::Schema
         | 
| 9 9 | 
             
              extend self
         | 
| 10 10 | 
             
              include ShopifyToolkit::MetafieldStatements
         | 
| 11 | 
            +
              include ShopifyToolkit::MetaobjectStatements
         | 
| 12 | 
            +
              include ShopifyToolkit::MetaobjectUtilities
         | 
| 11 13 | 
             
              include ShopifyToolkit::Migration::Logging
         | 
| 12 14 |  | 
| 13 15 | 
             
              delegate :logger, to: Rails
         | 
| @@ -50,8 +52,19 @@ module ShopifyToolkit::Schema | |
| 50 52 | 
             
                end
         | 
| 51 53 |  | 
| 52 54 | 
             
                announce "Loading metafield schema from #{path}"
         | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            +
             | 
| 56 | 
            +
                # Parse the schema file to separate metaobject and metafield definitions
         | 
| 57 | 
            +
                schema_content = File.read(path)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                say_with_time "Executing metaobject definitions" do
         | 
| 60 | 
            +
                  # Execute only metaobject definitions first
         | 
| 61 | 
            +
                  execute_metaobject_definitions(schema_content)
         | 
| 62 | 
            +
                  apply_pending_field_validations
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                say_with_time "Executing metafield definitions" do
         | 
| 66 | 
            +
                  # Execute only metafield definitions after all metaobjects exist
         | 
| 67 | 
            +
                  execute_metafield_definitions(schema_content)
         | 
| 55 68 | 
             
                end
         | 
| 56 69 | 
             
              end
         | 
| 57 70 |  | 
| @@ -69,6 +82,66 @@ module ShopifyToolkit::Schema | |
| 69 82 | 
             
                instance_eval(&block)
         | 
| 70 83 | 
             
              end
         | 
| 71 84 |  | 
| 85 | 
            +
              def convert_validations_gids_to_types(validations, metafield_type)
         | 
| 86 | 
            +
                return validations unless validations&.any? && is_metaobject_reference_type?(metafield_type)
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                validations.filter_map do |validation|
         | 
| 89 | 
            +
                  convert_metaobject_validation_to_type(validation)
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              private
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              def convert_metaobject_validation_to_type(validation)
         | 
| 96 | 
            +
                name = validation["name"]
         | 
| 97 | 
            +
                value = validation["value"]
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                return validation unless metaobject_gid_validation?(name)
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                parsed_value = parse_json_if_needed(value)
         | 
| 102 | 
            +
                convert_gids_to_types(validation, parsed_value)
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              def metaobject_gid_validation?(name)
         | 
| 106 | 
            +
                name.in?(["metaobject_definition_id", "metaobject_definition_ids"])
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              def convert_gids_to_types(validation, value)
         | 
| 110 | 
            +
                if value.is_a?(Array)
         | 
| 111 | 
            +
                  convert_array_gids_to_types(validation, value)
         | 
| 112 | 
            +
                else
         | 
| 113 | 
            +
                  convert_single_gid_to_type(validation, value)
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              def convert_array_gids_to_types(validation, gids)
         | 
| 118 | 
            +
                types = gids.filter_map { |gid| convert_gid_to_type_if_valid(gid) }
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                return nil unless types.any?
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                validation.merge("name" => "metaobject_definition_types", "value" => types)
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
              def convert_single_gid_to_type(validation, gid)
         | 
| 126 | 
            +
                type = convert_gid_to_type_if_valid(gid)
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                return nil unless type
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                validation.merge("name" => "metaobject_definition_type", "value" => type)
         | 
| 131 | 
            +
              end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
              def convert_gid_to_type_if_valid(gid)
         | 
| 134 | 
            +
                return gid unless gid&.start_with?("gid://shopify/MetaobjectDefinition/")
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                type = get_metaobject_definition_type_by_gid(gid)
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                if type.nil?
         | 
| 139 | 
            +
                  say "Warning: Metafield validation references unknown metaobject GID #{gid} - excluding from portable schema"
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                type
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
             | 
| 72 145 | 
             
              def fetch_definitions(owner_type:)
         | 
| 73 146 | 
             
                owner_type = owner_type.to_s.singularize.upcase
         | 
| 74 147 |  | 
| @@ -116,15 +189,64 @@ module ShopifyToolkit::Schema | |
| 116 189 | 
             
                result.dig("data", "metafieldDefinitions", "nodes") || []
         | 
| 117 190 | 
             
              end
         | 
| 118 191 |  | 
| 192 | 
            +
              def fetch_metaobject_definitions
         | 
| 193 | 
            +
                query = <<~GRAPHQL
         | 
| 194 | 
            +
                  query {
         | 
| 195 | 
            +
                    metaobjectDefinitions(first: 250) {
         | 
| 196 | 
            +
                      nodes {
         | 
| 197 | 
            +
                        id
         | 
| 198 | 
            +
                        type
         | 
| 199 | 
            +
                        name
         | 
| 200 | 
            +
                        description
         | 
| 201 | 
            +
                        fieldDefinitions {
         | 
| 202 | 
            +
                          key
         | 
| 203 | 
            +
                          name
         | 
| 204 | 
            +
                          description
         | 
| 205 | 
            +
                          type {
         | 
| 206 | 
            +
                            name
         | 
| 207 | 
            +
                          }
         | 
| 208 | 
            +
                          required
         | 
| 209 | 
            +
                          validations {
         | 
| 210 | 
            +
                            name
         | 
| 211 | 
            +
                            value
         | 
| 212 | 
            +
                          }
         | 
| 213 | 
            +
                        }
         | 
| 214 | 
            +
                        access {
         | 
| 215 | 
            +
                          admin
         | 
| 216 | 
            +
                          storefront
         | 
| 217 | 
            +
                        }
         | 
| 218 | 
            +
                        capabilities {
         | 
| 219 | 
            +
                          publishable {
         | 
| 220 | 
            +
                            enabled
         | 
| 221 | 
            +
                          }
         | 
| 222 | 
            +
                          translatable {
         | 
| 223 | 
            +
                            enabled
         | 
| 224 | 
            +
                          }
         | 
| 225 | 
            +
                        }
         | 
| 226 | 
            +
                      }
         | 
| 227 | 
            +
                    }
         | 
| 228 | 
            +
                  }
         | 
| 229 | 
            +
                GRAPHQL
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                result =
         | 
| 232 | 
            +
                  shopify_admin_client
         | 
| 233 | 
            +
                    .query(query:)
         | 
| 234 | 
            +
                    .tap { handle_shopify_admin_client_errors(_1) }
         | 
| 235 | 
            +
                    .body
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                result.dig("data", "metaobjectDefinitions", "nodes") || []
         | 
| 238 | 
            +
              end
         | 
| 239 | 
            +
             | 
| 119 240 | 
             
              def generate_schema_content
         | 
| 120 | 
            -
                 | 
| 241 | 
            +
                metaobject_definitions = fetch_metaobject_definitions
         | 
| 242 | 
            +
                metafield_definitions =
         | 
| 121 243 | 
             
                  OWNER_TYPES.flat_map { |owner_type| fetch_definitions(owner_type:) }
         | 
| 122 244 |  | 
| 123 245 | 
             
                content = StringIO.new
         | 
| 124 246 | 
             
                content << <<~RUBY
         | 
| 125 | 
            -
                  # This file is auto-generated from the current state of the Shopify metafields.
         | 
| 126 | 
            -
                  # Instead of editing this file, please use the  | 
| 127 | 
            -
                  # to incrementally modify your metafields, and then regenerate this schema definition.
         | 
| 247 | 
            +
                  # This file is auto-generated from the current state of the Shopify metafields and metaobjects.
         | 
| 248 | 
            +
                  # Instead of editing this file, please use the migration features of ShopifyToolkit
         | 
| 249 | 
            +
                  # to incrementally modify your metafields and metaobjects, and then regenerate this schema definition.
         | 
| 128 250 | 
             
                  #
         | 
| 129 251 | 
             
                  # This file is the source used to define your metafields when running `bin/rails shopify:schema:load`.
         | 
| 130 252 | 
             
                  #
         | 
| @@ -132,8 +254,59 @@ module ShopifyToolkit::Schema | |
| 132 254 | 
             
                  ShopifyToolkit::Schema.define do
         | 
| 133 255 | 
             
                RUBY
         | 
| 134 256 |  | 
| 135 | 
            -
                #  | 
| 136 | 
            -
                 | 
| 257 | 
            +
                # Add metaobject definitions first
         | 
| 258 | 
            +
                metaobject_definitions
         | 
| 259 | 
            +
                  .sort_by { _1["type"] }
         | 
| 260 | 
            +
                  .each do |definition|
         | 
| 261 | 
            +
                    type = definition["type"]
         | 
| 262 | 
            +
                    name = definition["name"]
         | 
| 263 | 
            +
                    description = definition["description"]
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                    field_definitions = definition["fieldDefinitions"]&.map do |field|
         | 
| 266 | 
            +
                      field_hash = {
         | 
| 267 | 
            +
                        key: field["key"].to_sym,
         | 
| 268 | 
            +
                        type: field["type"]["name"].to_sym,
         | 
| 269 | 
            +
                        name: field["name"]
         | 
| 270 | 
            +
                      }
         | 
| 271 | 
            +
                      field_hash[:description] = field["description"] if field["description"] && !field["description"].empty?
         | 
| 272 | 
            +
                      field_hash[:required] = field["required"] if field["required"] == true
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                      # Convert validations for metaobject reference fields within metaobjects
         | 
| 275 | 
            +
                      if field["validations"]&.any? && is_metaobject_reference_type?(field["type"]["name"])
         | 
| 276 | 
            +
                        field_hash[:validations] = convert_validations_gids_to_types(field["validations"], field["type"]["name"])&.map { |v| v.transform_keys(&:to_sym) }
         | 
| 277 | 
            +
                      elsif field["validations"]&.any?
         | 
| 278 | 
            +
                        field_hash[:validations] = field["validations"]&.map { |v| v.transform_keys(&:to_sym) }
         | 
| 279 | 
            +
                      end
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                      field_hash
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                    access = definition["access"]
         | 
| 285 | 
            +
                    capabilities = definition["capabilities"]
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                    args = [type.to_sym]
         | 
| 288 | 
            +
                    kwargs = { name: name }
         | 
| 289 | 
            +
                    kwargs[:description] = description if description && !description.empty?
         | 
| 290 | 
            +
                    kwargs[:field_definitions] = field_definitions if field_definitions&.any?
         | 
| 291 | 
            +
                    kwargs[:access] = access if access&.any?
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                    # Add capabilities if non-default
         | 
| 294 | 
            +
                    if capabilities&.any? { |_, v| v["enabled"] == true }
         | 
| 295 | 
            +
                      kwargs[:capabilities] = capabilities.transform_keys(&:to_sym).transform_values { |v| v.transform_keys(&:to_sym) }
         | 
| 296 | 
            +
                    end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                    args_string = args.map(&:inspect).join(", ")
         | 
| 299 | 
            +
                    kwargs_string = kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
         | 
| 300 | 
            +
                    content.puts "  create_metaobject_definition #{args_string}, #{kwargs_string}"
         | 
| 301 | 
            +
                  end
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                # Add blank line between metaobjects and metafields if both exist
         | 
| 304 | 
            +
                if metaobject_definitions.any? && metafield_definitions.any?
         | 
| 305 | 
            +
                  content.puts ""
         | 
| 306 | 
            +
                end
         | 
| 307 | 
            +
             | 
| 308 | 
            +
                # Add metafield definitions
         | 
| 309 | 
            +
                metafield_definitions
         | 
| 137 310 | 
             
                  .sort_by { [_1["ownerType"], _1["namespace"], _1["key"]] }
         | 
| 138 311 | 
             
                  .each do
         | 
| 139 312 | 
             
                    owner_type = _1["ownerType"].downcase.pluralize.to_sym
         | 
| @@ -142,7 +315,7 @@ module ShopifyToolkit::Schema | |
| 142 315 | 
             
                    name = _1["name"]
         | 
| 143 316 | 
             
                    namespace = _1["namespace"]&.to_sym
         | 
| 144 317 | 
             
                    description = _1["description"]
         | 
| 145 | 
            -
                    validations = _1["validations"]&.map { |v| v.transform_keys(&:to_sym) }
         | 
| 318 | 
            +
                    validations = convert_validations_gids_to_types(_1["validations"], type)&.map { |v| v.transform_keys(&:to_sym) }
         | 
| 146 319 | 
             
                    capabilities =
         | 
| 147 320 | 
             
                      _1["capabilities"]
         | 
| 148 321 | 
             
                        &.transform_keys(&:to_sym)
         | 
| @@ -152,10 +325,10 @@ module ShopifyToolkit::Schema | |
| 152 325 | 
             
                    kwargs = { name: name }
         | 
| 153 326 | 
             
                    kwargs[:namespace] = namespace if namespace && namespace != :custom
         | 
| 154 327 | 
             
                    kwargs[:description] = description if description
         | 
| 155 | 
            -
                    kwargs[:validations] = validations if validations | 
| 328 | 
            +
                    kwargs[:validations] = validations if validations&.any?
         | 
| 156 329 |  | 
| 157 330 | 
             
                    # Only include capabilities if they have non-default values
         | 
| 158 | 
            -
                    if capabilities | 
| 331 | 
            +
                    if capabilities&.any?
         | 
| 159 332 | 
             
                      has_non_default_capabilities =
         | 
| 160 333 | 
             
                        capabilities.any? do |cap, value|
         | 
| 161 334 | 
             
                          case cap
         | 
| @@ -176,4 +349,43 @@ module ShopifyToolkit::Schema | |
| 176 349 | 
             
                content.puts "end"
         | 
| 177 350 | 
             
                content.string
         | 
| 178 351 | 
             
              end
         | 
| 352 | 
            +
             | 
| 353 | 
            +
              def execute_metaobject_definitions(schema_content)
         | 
| 354 | 
            +
                # Create a filtered version that only includes metaobject definitions
         | 
| 355 | 
            +
                metaobject_content = filter_schema_content(schema_content, :metaobject)
         | 
| 356 | 
            +
                eval_schema_content(metaobject_content)
         | 
| 357 | 
            +
              end
         | 
| 358 | 
            +
             | 
| 359 | 
            +
              def execute_metafield_definitions(schema_content)
         | 
| 360 | 
            +
                # Create a filtered version that only includes metafield definitions
         | 
| 361 | 
            +
                metafield_content = filter_schema_content(schema_content, :metafield)
         | 
| 362 | 
            +
                eval_schema_content(metafield_content)
         | 
| 363 | 
            +
              end
         | 
| 364 | 
            +
             | 
| 365 | 
            +
              def filter_schema_content(schema_content, type)
         | 
| 366 | 
            +
                lines = schema_content.lines
         | 
| 367 | 
            +
                filtered_lines = []
         | 
| 368 | 
            +
             | 
| 369 | 
            +
                # Always include the header and footer
         | 
| 370 | 
            +
                filtered_lines << lines.first(8) # Header lines up to "ShopifyToolkit::Schema.define do"
         | 
| 371 | 
            +
                filtered_lines.flatten!
         | 
| 372 | 
            +
             | 
| 373 | 
            +
                lines.each do |line|
         | 
| 374 | 
            +
                  case type
         | 
| 375 | 
            +
                  when :metaobject
         | 
| 376 | 
            +
                    if line.strip.start_with?("create_metaobject_definition")
         | 
| 377 | 
            +
                      filtered_lines << line
         | 
| 378 | 
            +
                    end
         | 
| 379 | 
            +
                  when :metafield
         | 
| 380 | 
            +
                    filtered_lines << line if line.strip.start_with?("create_metafield")
         | 
| 381 | 
            +
                  end
         | 
| 382 | 
            +
                end
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                filtered_lines << "end\n" # Closing line
         | 
| 385 | 
            +
                filtered_lines.join
         | 
| 386 | 
            +
              end
         | 
| 387 | 
            +
             | 
| 388 | 
            +
              def eval_schema_content(content)
         | 
| 389 | 
            +
                instance_eval(content)
         | 
| 390 | 
            +
              end
         | 
| 179 391 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,15 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: shopify_toolkit
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.5. | 
| 4 | 
            +
              version: 0.5.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Elia Schito
         | 
| 8 8 | 
             
            - Nebulab Team
         | 
| 9 | 
            +
            autorequire:
         | 
| 9 10 | 
             
            bindir: exe
         | 
| 10 11 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2025- | 
| 12 | 
            +
            date: 2025-10-31 00:00:00.000000000 Z
         | 
| 12 13 | 
             
            dependencies:
         | 
| 13 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 15 | 
             
              name: railties
         | 
| @@ -94,6 +95,7 @@ dependencies: | |
| 94 95 | 
             
                - - ">="
         | 
| 95 96 | 
             
                  - !ruby/object:Gem::Version
         | 
| 96 97 | 
             
                    version: '14.8'
         | 
| 98 | 
            +
            description:
         | 
| 97 99 | 
             
            email:
         | 
| 98 100 | 
             
            - elia@schito.me
         | 
| 99 101 | 
             
            executables:
         | 
| @@ -112,6 +114,7 @@ files: | |
| 112 114 | 
             
            - lib/shopify_toolkit/command_line.rb
         | 
| 113 115 | 
             
            - lib/shopify_toolkit/metafield_statements.rb
         | 
| 114 116 | 
             
            - lib/shopify_toolkit/metaobject_statements.rb
         | 
| 117 | 
            +
            - lib/shopify_toolkit/metaobject_utilities.rb
         | 
| 115 118 | 
             
            - lib/shopify_toolkit/migration.rb
         | 
| 116 119 | 
             
            - lib/shopify_toolkit/migration/logging.rb
         | 
| 117 120 | 
             
            - lib/shopify_toolkit/migrator.rb
         | 
| @@ -127,6 +130,7 @@ metadata: | |
| 127 130 | 
             
              homepage_uri: https://github.com/nebulab/shopify_toolkit?tab=readme-ov-file#readme
         | 
| 128 131 | 
             
              source_code_uri: https://github.com/nebulab/shopify_toolkit
         | 
| 129 132 | 
             
              changelog_uri: https://github.com/nebulab/shopify_toolkit/releases
         | 
| 133 | 
            +
            post_install_message:
         | 
| 130 134 | 
             
            rdoc_options: []
         | 
| 131 135 | 
             
            require_paths:
         | 
| 132 136 | 
             
            - lib
         | 
| @@ -141,7 +145,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 141 145 | 
             
                - !ruby/object:Gem::Version
         | 
| 142 146 | 
             
                  version: '0'
         | 
| 143 147 | 
             
            requirements: []
         | 
| 144 | 
            -
            rubygems_version: 3. | 
| 148 | 
            +
            rubygems_version: 3.5.16
         | 
| 149 | 
            +
            signing_key:
         | 
| 145 150 | 
             
            specification_version: 4
         | 
| 146 151 | 
             
            summary: A collection of tools for dealing with Shopify apps.
         | 
| 147 152 | 
             
            test_files: []
         |