Use shrine to upload files instead of ActiveStorage

This commit is contained in:
Eugene Burmakin 2024-04-25 22:28:34 +02:00
parent d99e6d6f50
commit e00f614b9a
14 changed files with 64 additions and 26 deletions

16
Gemfile
View file

@ -5,31 +5,31 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.2.3'
gem 'bootsnap', require: false
gem 'chartkick'
gem 'devise'
gem 'geocoder'
gem 'importmap-rails'
gem 'pg'
gem 'puma'
gem 'pundit'
gem 'rails'
gem 'shrine', '~> 3.5'
gem 'sidekiq'
gem 'sidekiq-cron'
gem 'sprockets-rails'
gem 'stimulus-rails'
gem 'tailwindcss-rails'
gem 'turbo-rails'
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
gem 'importmap-rails'
gem 'chartkick'
gem 'geocoder'
gem 'sidekiq'
gem 'sidekiq-cron'
group :development, :test do
gem 'debug', platforms: %i[mri mingw x64_mingw]
gem 'dotenv-rails'
gem 'factory_bot_rails'
gem 'ffaker'
gem 'rspec-rails'
gem 'dotenv-rails'
gem 'pry-byebug'
gem 'pry-rails'
gem 'rspec-rails'
end
group :test do

View file

@ -75,6 +75,8 @@ GEM
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
attr_extras (7.1.0)
base64 (0.2.0)
@ -88,6 +90,7 @@ GEM
coderay (1.1.3)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
content_disposition (1.0.0)
crass (1.0.6)
date (3.3.4)
debug (1.9.2)
@ -105,6 +108,8 @@ GEM
dotenv-rails (3.1.0)
dotenv (= 3.1.0)
railties (>= 6.1)
down (5.4.2)
addressable (~> 2.8)
drb (2.2.1)
erubi (1.12.0)
et-orbi (1.2.11)
@ -189,6 +194,7 @@ GEM
pry (>= 0.10.4)
psych (5.1.2)
stringio
public_suffix (5.0.5)
puma (6.4.2)
nio4r (~> 2.0)
pundit (2.3.1)
@ -285,6 +291,9 @@ GEM
ruby-progressbar (1.13.0)
shoulda-matchers (6.2.0)
activesupport (>= 5.2.0)
shrine (3.5.0)
content_disposition (~> 1.0)
down (~> 5.1)
sidekiq (7.2.2)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
@ -372,6 +381,7 @@ DEPENDENCIES
rspec-rails
rubocop-rails
shoulda-matchers
shrine (~> 3.5)
sidekiq
sidekiq-cron
simplecov

File diff suppressed because one or more lines are too long

View file

@ -15,13 +15,14 @@ class ImportsController < ApplicationController
def create
files = import_params[:files].reject(&:blank?)
import_ids = files.map do |file|
import = current_user.imports.create(
name: file.original_filename,
source: params[:import][:source]
)
import.file.attach(file)
import.update(raw_data: JSON.parse(File.read(file)))
import.id
end

View file

@ -1,12 +1,11 @@
# frozen_string_literal: true
class ImportJob < ApplicationJob
queue_as :default
def perform(user_id, import_id)
user = User.find(user_id)
import = user.imports.find(import_id)
file = import.file
sleep 3 # It takes time to process uploaded file
result = parser(import.source).new(import).call

View file

@ -1,8 +1,10 @@
# frozen_string_literal: true
class Import < ApplicationRecord
belongs_to :user
has_many :points, dependent: :destroy
has_one_attached :file
include ImportUploader::Attachment(:raw)
enum source: { google: 0, owntracks: 1 }
end

View file

@ -1,11 +1,10 @@
# frozen_string_literal: true
class GoogleMaps::TimelineParser
attr_reader :import, :json
attr_reader :import
def initialize(import)
@import = import
@json = JSON.parse(import.file.download)
end
def call
@ -38,7 +37,7 @@ class GoogleMaps::TimelineParser
private
def parse_json
json['timelineObjects'].flat_map do |timeline_object|
import.raw_data['timelineObjects'].flat_map do |timeline_object|
if timeline_object['activitySegment'].present?
if timeline_object['activitySegment']['startLocation'].blank?
next if timeline_object['activitySegment']['waypointPath'].blank?
@ -61,7 +60,7 @@ class GoogleMaps::TimelineParser
end
elsif timeline_object['placeVisit'].present?
if timeline_object['placeVisit']['location']['latitudeE7'].present? &&
timeline_object['placeVisit']['location']['longitudeE7'].present?
timeline_object['placeVisit']['location']['longitudeE7'].present?
{
latitude: timeline_object['placeVisit']['location']['latitudeE7'].to_f / 10**7,
longitude: timeline_object['placeVisit']['location']['longitudeE7'].to_f / 10**7,

View file

@ -5,7 +5,7 @@ class OwnTracks::ExportParser
def initialize(import)
@import = import
@json = JSON.parse(import.file.download)
@json = import.raw_data
end
def call
@ -32,7 +32,7 @@ class OwnTracks::ExportParser
doubles = points_data.size - points
processed = points + doubles
{ raw_points: points_data.size, points: points, doubles: doubles, processed: processed }
{ raw_points: points_data.size, points:, doubles:, processed: }
end
private
@ -40,8 +40,8 @@ class OwnTracks::ExportParser
def parse_json
points = []
json.keys.each do |user|
json[user].keys.each do |devise|
json.each_key do |user|
json[user].each_key do |devise|
json[user][devise].each { |point| points << OwnTracks::Params.new(point).call }
end
end

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
class ImportUploader < Shrine
# plugins and uploading logic
end

13
config/shrine.rb Normal file
View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'shrine'
require 'shrine/storage/file_system'
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'), # temporary
store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads') # permanent
}
Shrine.plugin :activerecord # loads Active Record integration
Shrine.plugin :cached_attachment_data # enables retaining cached file across form redisplays
Shrine.plugin :restore_cached_data # extracts metadata for assigned cached files

View file

@ -0,0 +1,5 @@
class AddRawDataToImports < ActiveRecord::Migration[7.1]
def change
add_column :imports, :raw_data, :jsonb
end
end

3
db/schema.rb generated
View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_04_04_154959) do
ActiveRecord::Schema[7.1].define(version: 2024_04_25_200155) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -51,6 +51,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_04_154959) do
t.integer "raw_points", default: 0
t.integer "doubles", default: 0
t.integer "processed", default: 0
t.jsonb "raw_data"
t.index ["source"], name: "index_imports_on_source"
t.index ["user_id"], name: "index_imports_on_user_id"
end

View file

@ -1,7 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :import do
user
name { 'APRIL_2013.json' }
source { 1 }
raw_data { JSON.parse(File.read('spec/fixtures/files/owntracks/export.json')) }
end
end

View file

@ -1,13 +1,13 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe OwnTracks::ExportParser do
describe '#call' do
subject(:parser) { described_class.new(import).call }
let(:file_path) { 'spec/fixtures/files/owntracks/export.json' }
let(:file) { fixture_file_upload(file_path) }
let(:user) { create(:user) }
let(:import) { create(:import, user: user, file: file, name: File.basename(file.path)) }
let(:import) { create(:import, user:, name: 'owntracks_export.json') }
context 'when file exists' do
it 'creates points' do