Class: Spree::Export

Inherits:
Object
  • Object
show all
Includes:
NumberIdentifier, SingleStoreResource, VendorConcern
Defined in:
app/models/spree/export.rb

Constant Summary collapse

SUPPORTED_FILE_FORMATS =
%i[csv].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.available_modelsObject



192
193
194
# File 'app/models/spree/export.rb', line 192

def available_models
  available_types.map(&:model_class)
end

.available_typesObject



188
189
190
# File 'app/models/spree/export.rb', line 188

def available_types
  Spree.export_types
end

.model_classObject

eg. Spree::Exports::Products => Spree::Product

Raises:

  • (NameError)


201
202
203
204
205
206
207
# File 'app/models/spree/export.rb', line 201

def model_class
  klass = "Spree::#{to_s.demodulize.singularize}".safe_constantize

  raise NameError, "Missing model class for #{self}" unless klass

  klass
end

.type_for_model(model) ⇒ Object



196
197
198
# File 'app/models/spree/export.rb', line 196

def type_for_model(model)
  available_types.find { |type| type.model_class.to_s == model.to_s }
end

Instance Method Details

#build_csv_line(_record) ⇒ Object

Raises:

  • (NotImplementedError)


111
112
113
# File 'app/models/spree/export.rb', line 111

def build_csv_line(_record)
  raise NotImplementedError, 'build_csv_line must be implemented'
end

#csv_headersObject

Raises:

  • (NotImplementedError)


100
101
102
# File 'app/models/spree/export.rb', line 100

def csv_headers
  raise NotImplementedError, 'csv_headers must be implemented'
end

#current_abilityObject



170
171
172
# File 'app/models/spree/export.rb', line 170

def current_ability
  @current_ability ||= Spree.ability_class.new(user, { store: store })
end

#done?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'app/models/spree/export.rb', line 65

def done?
  attachment.present? && attachment.attached?
end

#event_serializer_classObject



61
62
63
# File 'app/models/spree/export.rb', line 61

def event_serializer_class
  'Spree::Api::V3::ExportSerializer'.safe_constantize
end

#export_file_nameObject

eg. Spree::Exports::Products => products-store-my-store-code-20241030133348.csv



175
176
177
# File 'app/models/spree/export.rb', line 175

def export_file_name
  "#{type.demodulize.underscore}-#{store.code}-#{created_at.strftime('%Y%m%d%H%M%S')}.#{format}"
end

#export_tmp_file_pathObject



179
180
181
# File 'app/models/spree/export.rb', line 179

def export_tmp_file_path
  Rails.root.join('tmp', export_file_name)
end

#generateObject



73
74
75
76
77
# File 'app/models/spree/export.rb', line 73

def generate
  send(:"generate_#{format}")
  handle_attachment
  send_export_done_email
end

#generate_asyncObject



69
70
71
# File 'app/models/spree/export.rb', line 69

def generate_async
  Spree::Exports::GenerateJob.perform_later(id)
end

#generate_csvObject



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/models/spree/export.rb', line 79

def generate_csv
  ::CSV.open(export_tmp_file_path, 'wb', encoding: 'UTF-8', col_sep: ',', row_sep: "\r\n") do |csv|
    csv << csv_headers
    records_to_export.includes(scope_includes).find_in_batches do |batch|
      batch.each do |record|
        if multi_line_csv?
          record.to_csv(store).each do |line|
            csv << line
          end
        else
          csv << record.to_csv(store)
        end
      end
    end
  end
end

#handle_attachmentObject



115
116
117
118
119
# File 'app/models/spree/export.rb', line 115

def handle_attachment
  file = ::File.open(export_tmp_file_path)
  attachment.attach(io: file, filename: export_file_name)
  ::File.delete(export_tmp_file_path) if ::File.exist?(export_tmp_file_path)
end

#metafields_headersArray<String>

Returns an array of metafield headers for the model

Returns:

  • (Array<String>)


107
108
109
# File 'app/models/spree/export.rb', line 107

def metafields_headers
  @metafields_headers ||= Spree::MetafieldDefinition.for_resource_type(model_class.to_s).order(:namespace, :key).map(&:csv_header_name)
end

#model_classObject

eg. Spree::Exports::Products => Spree::Product



141
142
143
144
145
146
147
# File 'app/models/spree/export.rb', line 141

def model_class
  if type == 'Spree::Exports::Customers'
    Spree.user_class
  else
    "Spree::#{type.demodulize.singularize}".constantize
  end
end

#multi_line_csv?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'app/models/spree/export.rb', line 96

def multi_line_csv?
  false
end

#normalize_search_paramsObject

Ensures search params maintain consistent format between UI and exports

  • Preserves valid JSON unchanged

  • Converts Ruby hashes to JSON strings

  • Handles malformed input gracefully



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'app/models/spree/export.rb', line 153

def normalize_search_params
  return if search_params.blank?

  if search_params.is_a?(Hash)
    self.search_params = search_params.to_json
    return
  end

  begin
    # It's a string, so we parse and re-dump to ensure consistency
    parsed = JSON.parse(search_params.to_s)
    self.search_params = parsed.to_json
  rescue JSON::ParserError
    # Leave as-is if not valid JSON string
  end
end

#records_to_exportObject



128
129
130
131
132
133
134
# File 'app/models/spree/export.rb', line 128

def records_to_export
  if search_params.present?
    scope.ransack(search_params.is_a?(String) ? JSON.parse(search_params.to_s).to_h : search_params)
  else
    scope.ransack
  end.result
end

#scopeObject



121
122
123
124
125
126
# File 'app/models/spree/export.rb', line 121

def scope
  scope = model_class
  scope = scope.for_store(store) if model_class.respond_to?(:for_store)
  scope = scope.for_vendor(vendor) if model_class.respond_to?(:for_vendor) && vendor.present?
  scope.accessible_by(current_ability)
end

#scope_includesObject



136
137
138
# File 'app/models/spree/export.rb', line 136

def scope_includes
  []
end

#send_export_done_emailObject



183
184
185
# File 'app/models/spree/export.rb', line 183

def send_export_done_email
  Spree::ExportMailer.export_done(self).deliver_later
end