2. Extracting metadata and processing attachments download_blob_to_tempfile, prepending callbacks 3. Validating the content of an attachment attachment_changes, attached?
URL and metadata from the package (app name, version, minimum OS version, …) 2. Create and upload a "manifest.plist" file with all these properties in XML format 3. Share a link to the manifest, not the package
after_create_commit { AnalyzeJob.perform_later(blob) } end # app/jobs/active_storage/analyze_job.rb class AnalyzeJob < BaseJob def perform(blob) # Pick an analyzer based on the blob content-type analyzer = Analyzer::ImageAnalyzer.new(blob) blob.update! metadata: analyzer.metadata end end # lib/active_storage/analyzer/image_analyzer.rb class Analyzer::ImageAnalyzer < Analyzer def metadata download_blob_to_tempfile do |file| image = MiniMagick::Image.new(file.path) { width: image.width, height: image.height } end end end 1. Whenever a model with an attachment is created, Rails enqueues a background job 2. The job looks for the right analyzer for the blob. Metadata returned by analyzer is stored in the DB 3. Analyzers run in a background job, so they must download the file to memory first. The file analysis is typically delegated to external libraries (MiniMagick, FFMpeg, …)
ActiveRecord::Base after_create_commit(prepend: true) do PackageAnalyzeJob.perform_later(self) end end # app/jobs/package_analyze_job.rb class PackageAnalyzeJob < ActiveStorage::BaseJob def perform(build) analyzer = PackageAnalyzer.new(build.package.blob) build.package.blob.update! metadata: analyzer.metadata end end # app/analyzers/package_analyzer.rb class PackageAnalyzer < ActiveStorage::Analyzer def metadata download_blob_to_tempfile do |file| CFPropertyBundle.new(file.path).properties end end end 2. The package analyzer is invoked and the returned metadata are stored in the database 3. The file is downloaded to memory and the analysis is run by the CFPropertyBundle library 1. Whenever a build with a package is created, enqueue a background job
ActiveRecord::Base after_create_commit(prepend: true) do PackageAnalyzeJob.perform_later(self) end end # app/jobs/package_analyze_job.rb class PackageAnalyzeJob < ActiveStorage::BaseJob def perform(build) analyzer = PackageAnalyzer.new(build.package.blob) build.package.blob.update! metadata: analyzer.metadata end end # app/analyzers/package_analyzer.rb class PackageAnalyzer < ActiveStorage::Analyzer def metadata download_blob_to_tempfile do |file| CFPropertyBundle.new(file.path).properties end end end 2. The package analyzer is invoked and the returned metadata are stored in the database 3. The file is downloaded to memory and the analysis is run by the CFPropertyBundle library 1. Whenever a build with a package is created, enqueue a background job what?
Error < StandardError; end def initialize(path) @path = path end def properties Zip::File.open(@path) do |package| data = extract_plist_data_from package { display_name: data['CFBundleDisplayName'], identifier: data['CFBundleIdentifier'], version: data['CFBundleVersion'], minimum_os_version: data['MinimumOSVersion'], short_version: data['CFBundleShortVersionString'] } end rescue Zip::Error, SystemCallError => e raise Error, e.message end end The CFPropertyBundle library unzips the package, parses the information stored in XML format and returns a Hash with the properties needed to generate a manifest. If the package is not a Zip file or the content doesn’t match the .ipa format, a custom CFProperty::Error is raised.
ActiveRecord::Base after_create_commit(prepend: true) do PackageAnalyzeJob.perform_later(self) end end # app/jobs/package_analyze_job.rb class PackageAnalyzeJob < ActiveStorage::BaseJob def perform(build) analyzer = PackageAnalyzer.new(build.package.blob) build.package.blob.update! metadata: analyzer.metadata end end # app/analyzers/package_analyzer.rb class PackageAnalyzer < ActiveStorage::Analyzer def metadata download_blob_to_tempfile do |file| CFPropertyBundle.new(file.path).properties end end end 2. The package analyzer is invoked and the returned metadata are stored in the database 3. The file is downloaded to memory and the analysis is run by the CFPropertyBundle library 1. Whenever a build with a package is created, enqueue a background job why?
ActiveRecord::Base after_create_commit(prepend: true) do PackageAnalyzeJob.perform_later(self) end end # app/jobs/package_analyze_job.rb class PackageAnalyzeJob < ActiveStorage::BaseJob def perform(build) analyzer = PackageAnalyzer.new(build.package.blob) build.package.blob.update! metadata: analyzer.metadata end end # app/analyzers/package_analyzer.rb class PackageAnalyzer < ActiveStorage::Analyzer def metadata download_blob_to_tempfile do |file| CFPropertyBundle.new(file.path).properties end end end 2. The package analyzer is invoked and the returned metadata are stored in the database 3. The file is downloaded to memory and the analysis is run by the CFPropertyBundle library 1. Whenever a build with a package is created, enqueue a background job the manifest can be generated here
perform(build) metadata = PackageAnalyzer.new(build.package.blob).metadata build.package.blob.update! metadata: metadata Tempfile.open ['manifest', '.plist'] do |file| file.write <<~MANIFEST <?xml version="1.0" encoding="UTF-8"?>[…] <key>url</key><string>#{CGI.escapeHTML build.package.service_url}</string> <key>title</key><string>#{metadata[:bundle_display_name]}</string> <key>bundle-version</key><string>#{metadata[:short_version]}</string>[…] MANIFEST build.manifest.attach io: File.open(file.path), filename: 'manifest.plist' end end end # app/models/build.rb class Build < ActiveRecord::Base has_one_attached :package has_one_attached :manifest end Attaching the manifest to the build grants access to all the convenient Active Storage methods (attach, rails_blob_url, …)
2. Extracting metadata and processing attachments download_blob_to_tempfile, prepending callbacks 3. Validating the content of an attachment attachment_changes, attached?