tessa 1.2.3 → 2.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: 455d8debb812aaaada729eddbfd157c9214db5da5fe0906f1e5debaf9a2ca4c1
4
- data.tar.gz: 40c27986ec183944f8c5544caa54ebd2a1a041685627b9ff5556eaa87e99afd6
3
+ metadata.gz: 5d98c3c1a4dd8795f5631bee2260de0c0794f4eded2c71c6cf4bfaee23dc518e
4
+ data.tar.gz: 42c2a4748df92707676e06ecbb965e5245444bf343e468a5db60ea55a6b89337
5
5
  SHA512:
6
- metadata.gz: ba3575a6182a0bf896338ab41df520096f608fd8df8040a542629e94dffeaf8255f2b6e03a282eb637bfe201e28c57e8cc68f60977f65a4acf92e878b7b0e2d5
7
- data.tar.gz: 635386ff8364bfe1a5608651666009f9c07bb1aad05cf8a76f7bcacf0c422a2baa9432fad697c9907d919375ce55ab4f057b143f908a643c63a5de41de5cbed8
6
+ metadata.gz: bd8d14042de56677d144e26ff425551d5e7a68e95d331406e1556fbb34ec43f305203e139150fa05647091caaabe38c4d2df5cf29889d783665a8806a15e9ef8
7
+ data.tar.gz: d25ed80bf643b5ff4a126d67094b0ba66caf4f643f90efdfd4acec0dbbbf555bf1f60609bc51bc6bdb633cf5fb86feac3cde3d1cfb26adc4e75856cf76c55103
data/Gemfile CHANGED
@@ -11,4 +11,5 @@ end
11
11
  group :test do
12
12
  gem 'rspec-rails'
13
13
  gem 'webmock'
14
+ gem 'faraday'
14
15
  end
data/README.md CHANGED
@@ -1,11 +1,27 @@
1
- # [Tessa][0]
1
+ # [Tessa Client][0]
2
2
 
3
3
  [ ![Gem Version][1] ][2]
4
4
  [ ![Build Status][3] ][4]
5
5
  [ ![Coverage Status][5] ][6]
6
6
  [ ![Code Climate][7] ][8]
7
7
 
8
- Manage your assets.
8
+ This gem wraps ActiveStorage to provide some convenience helpers for direct uploading
9
+ assets, as well as generating public and private asset URLs.
10
+
11
+ ## History
12
+
13
+ This gem was initially conceived as a client to the
14
+ [Tessa asset management service](https://github.com/watermarkchurch/tessa). That service has been deprecated and is
15
+ being phased out.
16
+
17
+ In 2022, we moved away from using Tessa to manage our assets and got onto the official Rails ActiveStorage library.
18
+ [Here is the issue tracking the effort](https://github.com/watermarkchurch/tessa/issues/27). As part of that process,
19
+ we released the v1.x version of this gem which supports both ActiveStorage and Tessa but uploads all new assets to
20
+ ActiveStorage.
21
+
22
+ When that migration is completed, v2.x of this gem will remove all code that accesses the old Tessa service. This gem
23
+ will transition to being simply a convenience wrapper around ActiveStorage, so that we don't have to re-write as much
24
+ of our code in our frontend apps.
9
25
 
10
26
  ## Installation
11
27
 
@@ -25,7 +41,65 @@ Or install it yourself as:
25
41
 
26
42
  ## Usage
27
43
 
28
- TODO: Write usage instructions here
44
+ We provide no guarantee of support for your use of this gem. It is for internal Watermark use only.
45
+ **Use at your own risk!**
46
+
47
+ The main thing this gem still gives us is direct-uploads using [Dropzone.js](https://github.com/dropzone/dropzone).
48
+ Before using this gem it's worthwhile to understand how it works:
49
+
50
+ ![Sequence Diagram](./docs/tessa-activestorage-sequence-diagram.drawio.png)
51
+
52
+ When an Asset input is configured using the SimpleForm helper (TODO: https://github.com/watermarkchurch/tessa-client/issues/22),
53
+ it creates a DropZone div with the css class `.tessa-upload`. The javascript
54
+ in `app/javascript/tessa/index.js.coffee` scans for these divs and configures
55
+ Dropzone.js.
56
+
57
+ When a user initiates a file upload via Dropzone, it does an XHR POST to `/tessa/uploads`. This triggers
58
+ the `lib/tessa/rack_upload_proxy.rb` middleware to create a new ActiveStorage blob, and returns the
59
+ signed upload URL. Dropzone then uploads the file directly to S3 (or wherever ActiveStorage is configured)
60
+ from the user's browser.
61
+
62
+ To get all this working, follow these steps:
63
+
64
+ 1. Mount the engine
65
+ in your `config/routes.rb`, `mount Tessa::Engine, at: '/'`. It's important that it is mounted at root.
66
+
67
+ You can use Authentication around the engine to prevent unauthorized uploads. With devise it's as simple as:
68
+ ```rb
69
+ authenticated :user do
70
+ mount Tessa::Engine, at: '/'
71
+ end
72
+ ```
73
+
74
+ 2. In your application.js, require the js libraries:
75
+ ```js
76
+ //= require dropzone
77
+ //= require tessa
78
+ ```
79
+ Note that this only works if you are using Sprockets.
80
+ If you are using another bundler, we don't support that yet.
81
+
82
+ 3. In your model, use the Tessa `asset` declaration instead of `has_one_attached`. The SimpleForm helper only works
83
+ with Tessa `asset` declarations so far.
84
+
85
+ ```rb
86
+ class Model < ApplicationRecord
87
+ include Tessa::Model
88
+
89
+ asset :image # Note: this essentially delegates to has_one_attached
90
+ ```
91
+
92
+ 4. When rendering your form, use the SimpleForm helper to render the dropzone div:
93
+
94
+ ```erb
95
+ <%= f.input :image,
96
+ as: :asset,
97
+ dropzone: { acceptedFiles: "image/*" },
98
+ hint: "Use an image that is 1440 x 288 in size (5:1 aspect ratio)" %>
99
+ ```
100
+
101
+ 5. Configure your ActiveStorage service to accept direct uploads.
102
+ The disk service does this automatically. The S3 service requires additional CORS configuration.
29
103
 
30
104
  ## Contributing
31
105
 
data/lib/tasks/tessa.rake CHANGED
@@ -2,6 +2,17 @@
2
2
  namespace :tessa do
3
3
  desc "Begins the migration of all Tessa assets to ActiveStorage."
4
4
  task :migrate => :environment do
5
- Tessa::MigrateAssetsJob.perform_later
5
+ abort "Tessa::MigrateAssetsJob can no longer be performed because the Tessa connection was removed. "\
6
+ "Please downgrade to tessa ~>1.0 and try again."
7
+ end
8
+
9
+ desc "Verifies that the migration has completed"
10
+ task :verify => :environment do
11
+ unless Tessa::MigrateAssetsJob.complete?
12
+ state = Tessa::MigrateAssetsJob::ProcessingState.initialize_from_models
13
+
14
+ abort "Tessa::MigrateAssetsJob not yet complete! #{state.count} records remain to be migrated. "\
15
+ "Please downgrade to tessa ~>1.0 and try again."
16
+ end
6
17
  end
7
18
  end
data/lib/tessa/config.rb CHANGED
@@ -10,15 +10,7 @@ module Tessa
10
10
  attribute :strategy, String, default: -> (*_) { ENV['TESSA_STRATEGY'] || DEFAULT_STRATEGY }
11
11
 
12
12
  def connection
13
- @connection ||= Faraday.new(url: url) do |conn|
14
- if conn.respond_to?(:basic_auth)
15
- conn.basic_auth username, password
16
- else # Faraday >= 1.0
17
- conn.request :authorization, :basic, username, password
18
- end
19
- conn.request :url_encoded
20
- conn.adapter Faraday.default_adapter
21
- end
13
+ @connection ||= Tessa::FakeConnection.new
22
14
  end
23
15
  end
24
16
  end
@@ -0,0 +1,29 @@
1
+ module Tessa
2
+ # Since we no longer connect to the Tessa service, fake out the Tessa connection
3
+ # so that it always returns 503
4
+ class FakeConnection
5
+
6
+ [:get, :head, :put, :post, :patch, :delete].each do |method|
7
+ define_method(method) do |*args|
8
+ if defined?(Bugsnag)
9
+ Bugsnag.notify("Tessa::FakeConnection##{method} invoked")
10
+ end
11
+ Tessa::FakeConnection::Response.new()
12
+ end
13
+ end
14
+
15
+ class Response
16
+ def success?
17
+ false
18
+ end
19
+
20
+ def status
21
+ 503
22
+ end
23
+
24
+ def body
25
+ '{ "error": "The Tessa connection is no longer implemented." }'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -72,15 +72,6 @@ class Tessa::MigrateAssetsJob < ActiveJob::Base
72
72
  reupload(record, field_state)
73
73
  Rails.logger.info("#{record.class}#{record.id}##{field_state.field_name}: success")
74
74
  field_state.success_count += 1
75
- rescue OpenURI::HTTPError => ex
76
- if ex.message == "404 Not Found"
77
- # clear out the field if the asset is missing
78
- record.public_send("#{field_state.tessa_field.name}=", nil)
79
- record.save!
80
- else
81
- Rails.logger.error("#{record.class}#{record.id}##{field_state.field_name}: error - #{ex}")
82
- end
83
- field_state.offset += 1
84
75
  rescue StandardError => ex
85
76
  Rails.logger.error("#{record.class}#{record.id}##{field_state.field_name}: error - #{ex}")
86
77
  field_state.offset += 1
@@ -136,18 +127,24 @@ class Tessa::MigrateAssetsJob < ActiveJob::Base
136
127
  end
137
128
 
138
129
  def load_models_from_registry
139
- Rails.application.eager_load!
140
-
141
- # Load all Tessa models that can have attachments (not form objects)
142
- models = Tessa.model_registry
143
- .select { |m| m.respond_to?(:has_one_attached) }
144
-
145
130
  # Initialize our Record Keeping object
146
- ProcessingState.initialize_from_models(models)
131
+ ProcessingState.initialize_from_models
132
+ end
133
+
134
+ # Determines whether the migrate asset job is completed. If true, running the
135
+ # job again will not do anything.
136
+ def self.complete?
137
+ ProcessingState.initialize_from_models.fully_processed?
147
138
  end
148
139
 
149
140
  ProcessingState = Struct.new(:model_queue, :batch_count) do
150
- def self.initialize_from_models(models)
141
+ def self.initialize_from_models(models = nil)
142
+ unless models
143
+ # Load all Tessa models that can have attachments (not form objects)
144
+ Rails.application.eager_load!
145
+ models = Tessa.model_registry.select { |m| m.respond_to?(:has_one_attached) }
146
+ end
147
+
151
148
  new(
152
149
  models.map do |model|
153
150
  ModelProcessingState.initialize_from_model(model)
@@ -22,19 +22,11 @@ class Tessa::DynamicExtensions
22
22
  if #{name}_attachment.present?
23
23
  return Tessa::ActiveStorage::AssetWrapper.new(#{name}_attachment)
24
24
  end
25
-
26
- # fall back to old Tessa fetch if not present
27
- if field = self.class.tessa_fields["#{name}".to_sym]
28
- @#{name} ||= fetch_tessa_remote_assets(field.id(on: self))
29
- end
30
25
  end
31
26
 
32
27
  def #{field.id_field}
33
28
  # Use the attachment's key
34
29
  return #{name}_attachment.key if #{name}_attachment.present?
35
-
36
- # fallback to Tessa's database column
37
- super
38
30
  end
39
31
 
40
32
  def #{name}=(attachable)
@@ -80,7 +72,6 @@ class Tessa::DynamicExtensions
80
72
 
81
73
  @#{name} ||= [
82
74
  *#{name}_attachments.map { |a| Tessa::ActiveStorage::AssetWrapper.new(a) },
83
- *fetch_tessa_remote_assets(tessa_ids)
84
75
  ]
85
76
  end
86
77
 
@@ -88,8 +79,6 @@ class Tessa::DynamicExtensions
88
79
  [
89
80
  # Use the attachment's key
90
81
  *#{name}_attachments.map(&:key),
91
- # include from Tessa's database column
92
- *super
93
82
  ]
94
83
  end
95
84
 
@@ -105,8 +94,8 @@ class Tessa::DynamicExtensions
105
94
  existing.destroy
106
95
  else
107
96
  ids = self.#{field.id_field}
108
- ids.delete(change.id.to_i)
109
- self.#{field.id_field} = ids.any? ? ids : nil
97
+ ids&.delete(change.id.to_i)
98
+ self.#{field.id_field} = ids&.any? ? ids : nil
110
99
  end
111
100
  end
112
101
  attachables.changes.select(&:add?).each do |change|
@@ -138,10 +127,7 @@ class Tessa::DynamicExtensions
138
127
  def build(mod)
139
128
  mod.class_eval <<~CODE, __FILE__, __LINE__ + 1
140
129
  attr_accessor :#{name}_id
141
- attr_writer :#{name}
142
- def #{name}
143
- @#{name} ||= fetch_tessa_remote_assets(#{name}_id)
144
- end
130
+ attr_accessor :#{name}
145
131
  CODE
146
132
  mod
147
133
  end
@@ -151,12 +137,9 @@ class Tessa::DynamicExtensions
151
137
  def build(mod)
152
138
  mod.class_eval <<~CODE, __FILE__, __LINE__ + 1
153
139
  attr_accessor :#{name}_ids
154
- attr_writer :#{name}
155
- def #{name}
156
- @#{name} ||= fetch_tessa_remote_assets(#{name}_ids)
157
- end
140
+ attr_accessor :#{name}
158
141
  CODE
159
142
  mod
160
143
  end
161
144
  end
162
- end
145
+ end
data/lib/tessa/model.rb CHANGED
@@ -8,8 +8,6 @@ module Tessa
8
8
  def self.included(base)
9
9
  base.send :include, InstanceMethods
10
10
  base.extend ClassMethods
11
- base.after_commit :apply_tessa_change_sets if base.respond_to?(:after_commit)
12
- base.before_destroy :remove_all_tessa_assets if base.respond_to?(:before_destroy)
13
11
 
14
12
  Tessa.model_registry << base
15
13
  end
@@ -21,9 +19,9 @@ module Tessa
21
19
  end
22
20
 
23
21
  def apply_tessa_change_sets
24
- pending_tessa_change_sets.delete_if do |_, change_set|
25
- change_set.apply
26
- end
22
+ # Pretend like the application was successful but we didn't do anything
23
+ # because everything is in ActiveStorage now
24
+ pending_tessa_change_sets.clear
27
25
  end
28
26
 
29
27
  def remove_all_tessa_assets
@@ -37,17 +35,9 @@ module Tessa
37
35
  end
38
36
 
39
37
  def fetch_tessa_remote_assets(ids)
38
+ # This should just always return Tessa::AssetFailure
40
39
  Tessa.find_assets(ids)
41
40
  end
42
-
43
- private def reapplying_asset?(field, change_set)
44
- additions = change_set.changes.select(&:add?)
45
-
46
- return false if additions.none?
47
- return false if change_set.changes.size > additions.size
48
-
49
- additions.all? { |a| field.ids(on: self).include?(a.id) }
50
- end
51
41
  end
52
42
 
53
43
  module ClassMethods
@@ -13,8 +13,6 @@ class Tessa::Upload::UploadsFile
13
13
  end
14
14
 
15
15
  def self.connection_factory
16
- Faraday.new do |conn|
17
- conn.adapter Faraday.default_adapter
18
- end
16
+ Tessa::FakeConnection.new
19
17
  end
20
18
  end
data/lib/tessa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tessa
2
- VERSION = "1.2.3"
2
+ VERSION = "2.0"
3
3
  end
data/lib/tessa.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  require "tessa/version"
2
2
 
3
- require "faraday"
4
3
  require "virtus"
5
4
  require "json"
6
5
 
6
+ require "tessa/fake_connection"
7
7
  require "tessa/config"
8
8
  require "tessa/response_factory"
9
9
  require "tessa/asset"
@@ -24,16 +24,11 @@ RSpec.shared_examples_for "remote call macro" do |method, path, return_type|
24
24
  end
25
25
 
26
26
  context "when response is not successful" do
27
- let(:faraday_stubs) {
28
- Faraday::Adapter::Test::Stubs.new do |stub|
29
- stub.send(method, path) { |env| [422, {}, { "error" => "error" }.to_json] }
30
- end
31
- }
27
+ let(:connection) { Tessa::FakeConnection.new }
32
28
 
33
29
  it "raises Tessa::RequestFailed" do
34
30
  expect{ call }.to raise_error { |error|
35
31
  expect(error).to be_a(Tessa::RequestFailed)
36
- expect(error.response).to be_a(Faraday::Response)
37
32
  }
38
33
  end
39
34
  end
@@ -67,46 +67,4 @@ RSpec.describe Tessa::Config do
67
67
  expect(cfg.strategy).to eq("my-new-value")
68
68
  end
69
69
  end
70
-
71
- describe "#connection" do
72
- it "is a Faraday::Connection" do
73
- expect(cfg.connection).to be_a(Faraday::Connection)
74
- end
75
-
76
- context "with values cfgured" do
77
- subject { cfg.connection }
78
- before { args.each { |k, v| cfg.send("#{k}=", v) } }
79
- let(:args) {
80
- {
81
- url: "http://tessa.test",
82
- username: "username",
83
- password: "password",
84
- }
85
- }
86
-
87
- it "sets faraday's url prefix to our url" do
88
- expect(subject.url_prefix.to_s).to match(cfg.url)
89
- end
90
-
91
- context "with faraday spy" do
92
- let(:spy) { instance_spy(Faraday::Connection) }
93
- before do
94
- expect(Faraday).to receive(:new).and_yield(spy)
95
- subject
96
- end
97
-
98
- it "sets up url_encoded request handler" do
99
- expect(spy).to have_received(:request).with(:url_encoded)
100
- end
101
-
102
- it "cfgures the default adapter" do
103
- expect(spy).to have_received(:adapter).with(:net_http)
104
- end
105
- end
106
- end
107
-
108
- it "caches the result" do
109
- expect(cfg.connection.object_id).to eq(cfg.connection.object_id)
110
- end
111
- end
112
70
  end
@@ -93,17 +93,10 @@ RSpec.describe Tessa::Model do
93
93
  let(:instance) { model.new(another_place: []) }
94
94
  subject(:getter) { instance.multiple_field }
95
95
 
96
- it "calls find for each of the file_ids and returns result" do
96
+ it "No longer calls Tessa::Asset#find" do
97
97
  instance.another_place = [1, 2, 3]
98
- expect(Tessa::Asset).to receive(:find).with([1, 2, 3]).and_return([:a1, :a2, :a3])
99
- expect(getter).to eq([:a1, :a2, :a3])
100
- end
101
-
102
- it "caches the result" do
103
- instance.another_place = [1]
104
- expect(Tessa::Asset).to receive(:find).and_return(:val).once
105
- instance.multiple_field
106
- instance.multiple_field
98
+ expect(Tessa::Asset).to_not receive(:find)
99
+ expect(getter).to eq([])
107
100
  end
108
101
 
109
102
  context "with no values" do
@@ -121,17 +114,10 @@ RSpec.describe Tessa::Model do
121
114
  }
122
115
  subject(:getter) { instance.avatar }
123
116
 
124
- it "calls find for file_id and returns result" do
125
- instance.avatar_id = 1
126
- expect(Tessa::Asset).to receive(:find).with(1).and_return(:a1)
127
- expect(getter).to eq(:a1)
128
- end
129
-
130
- it "caches the result" do
117
+ it "No longer calls Tessa::Asset#find" do
131
118
  instance.avatar_id = 1
132
- expect(Tessa::Asset).to receive(:find).and_return(:val).once
133
- instance.avatar
134
- instance.avatar
119
+ expect(Tessa::Asset).to_not receive(:find)
120
+ expect(getter).to eq(nil)
135
121
  end
136
122
 
137
123
  it "wraps ActiveStorage uploads with AssetWrapper" do
@@ -334,245 +320,6 @@ RSpec.describe Tessa::Model do
334
320
 
335
321
  expect(instance.another_place).to eq(keys)
336
322
  end
337
-
338
- it 'replaces Tessa assets with ActiveStorage assets' do
339
- # Before deploying this code, we previously had DB records with Tessa IDs
340
- instance.update!(another_place: [1, 2, 3])
341
-
342
- # In this HTTP POST, we removed one of the tessa assets and uploaded a
343
- # new ActiveStorage asset
344
- blob = ::ActiveStorage::Blob.create_before_direct_upload!({
345
- filename: 'README.md',
346
- byte_size: file.size,
347
- content_type: file.content_type,
348
- checksum: '1234'
349
- })
350
- changeset = Tessa::AssetChangeSet.new(
351
- changes: [
352
- { 'id' => 1, 'action' => 'add' },
353
- { 'id' => 2, 'action' => 'remove' },
354
- { 'id' => 3, 'action' => 'add' },
355
- { 'id' => blob.signed_id, 'action' => 'add' },
356
- ]
357
- )
358
-
359
- # We'll download these assets when we access #multiple_field
360
- allow(Tessa.config.connection).to receive(:get)
361
- .with("/assets/1,3")
362
- .and_return(double("response",
363
- success?: true,
364
- body: [
365
- { 'id' => 1, 'public_url' => 'test1' },
366
- { 'id' => 2, 'public_url' => 'test2' }
367
- ].to_json))
368
-
369
- blob.upload(file)
370
-
371
- # act
372
- instance.multiple_field = changeset
373
-
374
- expect(instance.another_place).to eq([
375
- blob.key, 1, 3
376
- ])
377
-
378
- assets = instance.multiple_field
379
- expect(assets[0].key).to eq(blob.key)
380
- expect(assets[0].service_url)
381
- .to start_with('https://www.example.com/rails/active_storage/disk/')
382
-
383
- expect(assets[1].id).to eq(1)
384
- expect(assets[1].public_url).to eq('test1')
385
- expect(assets[2].id).to eq(2)
386
- expect(assets[2].public_url).to eq('test2')
387
- end
388
- end
389
- end
390
-
391
- describe "#apply_tessa_change_sets" do
392
- let(:instance) { model.new }
393
- let(:sets) { [ instance_spy(Tessa::AssetChangeSet) ] }
394
-
395
- before do
396
- instance.instance_variable_set(
397
- :@pending_tessa_change_sets,
398
- {
399
- avatar: sets[0],
400
- }
401
- )
402
- end
403
-
404
- it "iterates over all pending changesets calling apply" do
405
- instance.apply_tessa_change_sets
406
- expect(sets[0]).to have_received(:apply)
407
- end
408
-
409
- it "removes all changesets from list" do
410
- instance.apply_tessa_change_sets
411
- expect(instance.pending_tessa_change_sets).to be_empty
412
- end
413
-
414
- context "no @pending_tessa_change_sets ivar" do
415
- before do
416
- instance.instance_variable_set(
417
- :@pending_tessa_change_sets,
418
- nil
419
- )
420
- end
421
-
422
- it "doesn't raise error" do
423
- expect { instance.apply_tessa_change_sets }.to_not raise_error
424
- end
425
323
  end
426
324
  end
427
-
428
- describe "#fetch_tessa_remote_assets" do
429
- subject(:result) { model.new.fetch_tessa_remote_assets(arg) }
430
-
431
- context "argument is `nil`" do
432
- let(:arg) { nil }
433
-
434
- it "returns nil" do
435
- expect(result).to be_nil
436
- end
437
- end
438
-
439
- context "argument is `[]`" do
440
- let(:arg) { [] }
441
-
442
- it "returns []" do
443
- expect(result).to be_a(Array)
444
- expect(result).to be_empty
445
- end
446
- end
447
-
448
- context "when argument is not blank" do
449
- let(:id) { rand(100) }
450
- let(:arg) { id }
451
-
452
- it "calls Tessa::Asset.find with arguments" do
453
- expect(Tessa::Asset).to receive(:find).with(arg)
454
- result
455
- end
456
-
457
- context "when Tessa::Asset.find raises RequestFailed exception" do
458
- let(:error) {
459
- Tessa::RequestFailed.new("test exception", double(status: '500'))
460
- }
461
-
462
- before do
463
- allow(Tessa::Asset).to receive(:find).and_raise(error)
464
- end
465
-
466
- context "argument is single id" do
467
- let(:arg) { id }
468
-
469
- it "returns Failure" do
470
- expect(result).to be_a(Tessa::Asset::Failure)
471
- end
472
-
473
- it "returns asset with proper data" do
474
- expect(result.id).to eq(arg)
475
- end
476
- end
477
-
478
- context "argument is array" do
479
- let(:arg) { [ id, id * 2 ] }
480
-
481
- it "returns array" do
482
- expect(result).to be_a(Array)
483
- end
484
-
485
- it "returns instances of Failure" do
486
- expect(result).to all( be_a(Tessa::Asset::Failure) )
487
- end
488
-
489
- it "returns array with an asset for each id passed" do
490
- arg.zip(result) do |a, r|
491
- expect(r.id).to eq(a)
492
- end
493
- end
494
- end
495
- end
496
- end
497
- end
498
-
499
- describe "#remove_all_tessa_assets" do
500
- let(:instance) { model.new }
501
-
502
- context "with a single typed field" do
503
- let(:model) {
504
- SingleAssetModel
505
- }
506
-
507
- before do
508
- instance.avatar_id = 1
509
- end
510
-
511
- it "adds pending change sets for each field removing all current assets" do
512
- instance.remove_all_tessa_assets
513
- changes = instance.pending_tessa_change_sets.values
514
- .reduce(Tessa::AssetChangeSet.new, :+)
515
- .changes
516
- .map { |change| [change.id, change.action.to_sym] }
517
- expect(changes).to eq([
518
- [1, :remove]
519
- ])
520
- end
521
- end
522
-
523
-
524
- context "with a multiple typed field" do
525
- let(:model) {
526
- MultipleAssetModel
527
- }
528
- let(:instance) { model.new(another_place: []) }
529
-
530
- before do
531
- instance.another_place = [2, 3]
532
- end
533
-
534
- it "adds pending change sets for each field removing all current assets" do
535
- instance.remove_all_tessa_assets
536
- changes = instance.pending_tessa_change_sets.values
537
- .reduce(Tessa::AssetChangeSet.new, :+)
538
- .changes
539
- .map { |change| [change.id, change.action.to_sym] }
540
- expect(changes).to eq([
541
- [2, :remove],
542
- [3, :remove],
543
- ])
544
- end
545
- end
546
- end
547
-
548
- describe "adds callbacks" do
549
- context "model responds to after_commit" do
550
- let(:model) {
551
- Class.new do
552
- def self.after_commit(arg=nil)
553
- @after_commit ||= arg
554
- end
555
- end.tap { |c| c.send(:include, described_module) }
556
- }
557
-
558
- it "calls it with :apply_tessa_change_sets" do
559
- expect(model.after_commit).to eq(:apply_tessa_change_sets)
560
- end
561
- end
562
-
563
- context "model responds to before_destroy" do
564
- let(:model) {
565
- Class.new do
566
- def self.before_destroy(arg=nil)
567
- @before_destroy ||= arg
568
- end
569
- end.tap { |c| c.send(:include, described_module) }
570
- }
571
-
572
- it "calls it with :remove_all_tessa_assets" do
573
- expect(model.before_destroy).to eq(:remove_all_tessa_assets)
574
- end
575
- end
576
- end
577
-
578
325
  end
@@ -0,0 +1,23 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Tessa do
4
+
5
+ describe '#find_assets' do
6
+ it 'returns AssetFailure for singular asset' do
7
+ result = Tessa.find_assets(1)
8
+
9
+ expect(result).to be_a(Tessa::Asset::Failure)
10
+ expect(result.message).to eq("The service is unavailable at this time.")
11
+ end
12
+
13
+ it 'returns AssetFailure array for multiple assets' do
14
+ result = Tessa.find_assets([1, 2, 3])
15
+
16
+ expect(result.count).to eq(3)
17
+ result.each do |r|
18
+ expect(r).to be_a(Tessa::Asset::Failure)
19
+ expect(r.message).to eq("The service is unavailable at this time.")
20
+ end
21
+ end
22
+ end
23
+ end
data/tessa.gemspec CHANGED
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_dependency "faraday"
27
26
  spec.add_dependency "virtus", "~>1.0.4"
28
27
 
29
28
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tessa
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: '2.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Powell
@@ -9,22 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-03-20 00:00:00.000000000 Z
12
+ date: 2023-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: faraday
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '0'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '0'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: virtus
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -151,6 +137,7 @@ files:
151
137
  - app/javascript/tessa/index.js.coffee
152
138
  - bin/rspec
153
139
  - config/routes.rb
140
+ - docs/tessa-activestorage-sequence-diagram.drawio.png
154
141
  - lib/tasks/tessa.rake
155
142
  - lib/tessa.rb
156
143
  - lib/tessa/active_storage/asset_wrapper.rb
@@ -161,6 +148,7 @@ files:
161
148
  - lib/tessa/config.rb
162
149
  - lib/tessa/controller_helpers.rb
163
150
  - lib/tessa/engine.rb
151
+ - lib/tessa/fake_connection.rb
164
152
  - lib/tessa/jobs/migrate_assets_job.rb
165
153
  - lib/tessa/model.rb
166
154
  - lib/tessa/model/dynamic_extensions.rb
@@ -250,6 +238,7 @@ files:
250
238
  - spec/tessa/rack_upload_proxy_spec.rb
251
239
  - spec/tessa/upload/uploads_file_spec.rb
252
240
  - spec/tessa/upload_spec.rb
241
+ - spec/tessa_spec.rb
253
242
  - tessa.gemspec
254
243
  - yarn.lock
255
244
  homepage: https://github.com/watermarkchurch/tessa-client
@@ -351,3 +340,4 @@ test_files:
351
340
  - spec/tessa/rack_upload_proxy_spec.rb
352
341
  - spec/tessa/upload/uploads_file_spec.rb
353
342
  - spec/tessa/upload_spec.rb
343
+ - spec/tessa_spec.rb