tessa 2.0 → 6.0.0.rc2
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 +11 -18
- data/app/assets/javascripts/tessa.esm.js +212 -173
- data/app/assets/javascripts/tessa.js +206 -167
- data/app/javascript/activestorage/file_checksum.ts +76 -0
- data/app/javascript/tessa/index.ts +264 -0
- data/app/javascript/tessa/types.ts +34 -0
- data/config/routes.rb +0 -1
- data/lib/tessa/simple_form/asset_input.rb +24 -25
- data/lib/tessa/version.rb +1 -1
- data/lib/tessa.rb +0 -80
- data/package.json +7 -2
- data/rollup.config.js +5 -5
- data/spec/dummy/app/models/single_asset_model.rb +1 -1
- data/spec/dummy/app/models/single_asset_model_form.rb +3 -5
- data/spec/dummy/bin/rails +2 -2
- data/spec/dummy/bin/rake +2 -2
- data/spec/dummy/bin/setup +14 -6
- data/spec/dummy/bin/yarn +9 -3
- data/spec/dummy/config/application.rb +11 -9
- data/spec/dummy/config/boot.rb +1 -1
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +34 -5
- data/spec/dummy/config/environments/production.rb +49 -10
- data/spec/dummy/config/environments/test.rb +28 -12
- data/spec/dummy/config/initializers/backtrace_silencers.rb +4 -3
- data/spec/dummy/config/initializers/content_security_policy.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
- data/spec/dummy/config/initializers/new_framework_defaults_6_1.rb +67 -0
- data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +5 -0
- data/spec/dummy/config/locales/en.yml +1 -1
- data/spec/dummy/config/routes.rb +1 -1
- data/spec/dummy/config/storage.yml +31 -0
- data/spec/dummy/config.ru +2 -1
- data/spec/dummy/db/migrate/20230406194400_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
- data/spec/dummy/db/migrate/20230406194401_create_active_storage_variant_records.active_storage.rb +27 -0
- data/spec/dummy/db/schema.rb +15 -7
- data/tessa.gemspec +4 -5
- data/tsconfig.json +10 -0
- data/yarn.lock +74 -7
- metadata +36 -74
- data/app/javascript/activestorage/file_checksum.js +0 -53
- data/app/javascript/tessa/index.js.coffee +0 -183
- data/lib/tasks/tessa.rake +0 -18
- data/lib/tessa/active_storage/asset_wrapper.rb +0 -32
- data/lib/tessa/asset/failure.rb +0 -37
- data/lib/tessa/asset.rb +0 -47
- data/lib/tessa/asset_change.rb +0 -49
- data/lib/tessa/asset_change_set.rb +0 -49
- data/lib/tessa/config.rb +0 -16
- data/lib/tessa/controller_helpers.rb +0 -16
- data/lib/tessa/fake_connection.rb +0 -29
- data/lib/tessa/jobs/migrate_assets_job.rb +0 -222
- data/lib/tessa/model/dynamic_extensions.rb +0 -145
- data/lib/tessa/model/field.rb +0 -77
- data/lib/tessa/model.rb +0 -94
- data/lib/tessa/rack_upload_proxy.rb +0 -41
- data/lib/tessa/response_factory.rb +0 -15
- data/lib/tessa/upload/uploads_file.rb +0 -18
- data/lib/tessa/upload.rb +0 -31
- data/lib/tessa/view_helpers.rb +0 -23
- data/spec/dummy/app/models/multiple_asset_model.rb +0 -8
- data/spec/support/remote_call_macro.rb +0 -40
- data/spec/tessa/asset/failure_spec.rb +0 -48
- data/spec/tessa/asset_change_set_spec.rb +0 -198
- data/spec/tessa/asset_change_spec.rb +0 -86
- data/spec/tessa/asset_spec.rb +0 -196
- data/spec/tessa/config_spec.rb +0 -70
- data/spec/tessa/controller_helpers_spec.rb +0 -55
- data/spec/tessa/jobs/migrate_assets_job_spec.rb +0 -247
- data/spec/tessa/model_field_spec.rb +0 -72
- data/spec/tessa/model_spec.rb +0 -325
- data/spec/tessa/rack_upload_proxy_spec.rb +0 -83
- data/spec/tessa/upload/uploads_file_spec.rb +0 -72
- data/spec/tessa/upload_spec.rb +0 -125
- data/spec/tessa_spec.rb +0 -23
data/lib/tessa/model.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'tessa/model/field'
|
2
|
-
require 'tessa/model/dynamic_extensions'
|
3
|
-
require 'tessa/active_storage/asset_wrapper'
|
4
|
-
|
5
|
-
module Tessa
|
6
|
-
module Model
|
7
|
-
|
8
|
-
def self.included(base)
|
9
|
-
base.send :include, InstanceMethods
|
10
|
-
base.extend ClassMethods
|
11
|
-
|
12
|
-
Tessa.model_registry << base
|
13
|
-
end
|
14
|
-
|
15
|
-
module InstanceMethods
|
16
|
-
|
17
|
-
def pending_tessa_change_sets
|
18
|
-
@pending_tessa_change_sets ||= Hash.new { AssetChangeSet.new }
|
19
|
-
end
|
20
|
-
|
21
|
-
def apply_tessa_change_sets
|
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
|
25
|
-
end
|
26
|
-
|
27
|
-
def remove_all_tessa_assets
|
28
|
-
self.class.tessa_fields.each do |name, field|
|
29
|
-
change_set = pending_tessa_change_sets[name]
|
30
|
-
field.ids(on: self).each do |asset_id|
|
31
|
-
change_set.remove(asset_id)
|
32
|
-
end
|
33
|
-
pending_tessa_change_sets[name] = change_set
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def fetch_tessa_remote_assets(ids)
|
38
|
-
# This should just always return Tessa::AssetFailure
|
39
|
-
Tessa.find_assets(ids)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
module ClassMethods
|
44
|
-
def asset(name, args={})
|
45
|
-
field = tessa_fields[name] = Field.new(args.merge(name: name))
|
46
|
-
|
47
|
-
multiple = args[:multiple]
|
48
|
-
|
49
|
-
|
50
|
-
if respond_to?(:has_one_attached)
|
51
|
-
if multiple
|
52
|
-
has_many_attached(name)
|
53
|
-
else
|
54
|
-
has_one_attached(name)
|
55
|
-
end
|
56
|
-
|
57
|
-
# We have to replace the after_destroy_commit callback added above
|
58
|
-
callbacks = get_callbacks(:commit)
|
59
|
-
callbacks.delete(callbacks.to_a.last)
|
60
|
-
after_destroy_commit { public_send("#{name}_attachment")&.purge_later }
|
61
|
-
end
|
62
|
-
|
63
|
-
dynamic_extensions =
|
64
|
-
if respond_to?(:has_one_attached)
|
65
|
-
if multiple
|
66
|
-
::Tessa::DynamicExtensions::MultipleRecord.new(field)
|
67
|
-
else
|
68
|
-
::Tessa::DynamicExtensions::SingleRecord.new(field)
|
69
|
-
end
|
70
|
-
else
|
71
|
-
if multiple
|
72
|
-
::Tessa::DynamicExtensions::MultipleFormObject.new(field)
|
73
|
-
else
|
74
|
-
::Tessa::DynamicExtensions::SingleFormObject.new(field)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
include dynamic_extensions.build(Module.new)
|
78
|
-
|
79
|
-
# Undefine the activestorage default attribute method so it falls back
|
80
|
-
# to our dynamic module
|
81
|
-
remove_method "#{name}" rescue nil
|
82
|
-
remove_method "#{name}=" rescue nil
|
83
|
-
end
|
84
|
-
|
85
|
-
def tessa_fields
|
86
|
-
@tessa_fields ||= {}
|
87
|
-
end
|
88
|
-
|
89
|
-
def inherited(subclass)
|
90
|
-
subclass.instance_variable_set(:@tessa_fields, @tessa_fields.dup)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Tessa
|
2
|
-
class RackUploadProxy
|
3
|
-
|
4
|
-
def call(env)
|
5
|
-
request = Rack::Request.new(env)
|
6
|
-
::ActiveStorage::Current.host ||= request.base_url
|
7
|
-
|
8
|
-
# Call in to ActiveStorage to create a DirectUpload blob
|
9
|
-
params = env['rack.request.form_hash']
|
10
|
-
|
11
|
-
blob = ::ActiveStorage::Blob.create_before_direct_upload!({
|
12
|
-
filename: params["name"],
|
13
|
-
byte_size: params["size"],
|
14
|
-
content_type: params["mime_type"],
|
15
|
-
checksum: params["checksum"]
|
16
|
-
})
|
17
|
-
|
18
|
-
env['rack.session'][:tessa_upload_asset_ids] ||= []
|
19
|
-
env['rack.session'][:tessa_upload_asset_ids] << blob.signed_id
|
20
|
-
|
21
|
-
response = {
|
22
|
-
asset_id: blob.signed_id,
|
23
|
-
upload_url: blob.service_url_for_direct_upload,
|
24
|
-
upload_method: 'PUT', # ActiveStorage is always PUT
|
25
|
-
upload_headers: blob.service_headers_for_direct_upload
|
26
|
-
}
|
27
|
-
|
28
|
-
[200, {"Content-Type" => "application/json"}, [response.to_json]]
|
29
|
-
rescue Tessa::RequestFailed
|
30
|
-
[500, {"Content-Type" => "application/json"}, [{ "error" => "Failed to retreive upload URL" }.to_json]]
|
31
|
-
rescue ActiveRecord::NotNullViolation => e
|
32
|
-
[400, {"Content-Type" => "application/json"}, [{ "error" => e.message }.to_json]]
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.call(*args)
|
36
|
-
new.call(*args)
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Tessa
|
2
|
-
module ResponseFactory
|
3
|
-
|
4
|
-
def new_from_response(response)
|
5
|
-
raise RequestFailed.new("Tessa responded with #{response.status}", response) unless response.success?
|
6
|
-
case json = JSON.parse(response.body)
|
7
|
-
when Array
|
8
|
-
json.map { |record| new record }
|
9
|
-
when Hash
|
10
|
-
new json
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
end
|
15
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class Tessa::Upload::UploadsFile
|
2
|
-
attr_reader :upload, :connection
|
3
|
-
|
4
|
-
def initialize(upload:, connection: self.class.connection_factory)
|
5
|
-
@upload = upload
|
6
|
-
@connection = connection
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(file)
|
10
|
-
connection
|
11
|
-
.public_send(upload.upload_method, upload.upload_url, File.read(file))
|
12
|
-
.success?
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.connection_factory
|
16
|
-
Tessa::FakeConnection.new
|
17
|
-
end
|
18
|
-
end
|
data/lib/tessa/upload.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module Tessa
|
2
|
-
class Upload
|
3
|
-
include Virtus.model
|
4
|
-
extend ResponseFactory
|
5
|
-
|
6
|
-
attribute :asset_id, Integer
|
7
|
-
attribute :upload_url, String
|
8
|
-
attribute :upload_method, String
|
9
|
-
|
10
|
-
def upload_file(file)
|
11
|
-
if UploadsFile.new(upload: self).(file)
|
12
|
-
asset.complete!
|
13
|
-
else
|
14
|
-
asset.cancel!
|
15
|
-
false
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.create(connection: Tessa.config.connection,
|
20
|
-
strategy: Tessa.config.strategy,
|
21
|
-
**options)
|
22
|
-
new_from_response connection.post('/uploads', options.merge(strategy: strategy))
|
23
|
-
end
|
24
|
-
|
25
|
-
private def asset
|
26
|
-
Asset.new(id: asset_id)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
require 'tessa/upload/uploads_file'
|
data/lib/tessa/view_helpers.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Tessa
|
2
|
-
module ViewHelpers
|
3
|
-
def tessa_image_tag(asset, private: false)
|
4
|
-
handle_asset_failure(asset) do
|
5
|
-
image_tag(
|
6
|
-
private ? asset.private_url : asset.public_url
|
7
|
-
)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def handle_asset_failure(asset)
|
14
|
-
if asset.failure?
|
15
|
-
content_tag(:div, asset.message, class: "tessa-asset-failure")
|
16
|
-
else
|
17
|
-
yield
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
ActionView::Base.send :include, Tessa::ViewHelpers if defined?(ActionView)
|
@@ -1,40 +0,0 @@
|
|
1
|
-
RSpec.shared_examples_for "remote call macro" do |method, path, return_type|
|
2
|
-
let(:remote_response) { {} }
|
3
|
-
let(:faraday_stubs) {
|
4
|
-
Faraday::Adapter::Test::Stubs.new do |stub|
|
5
|
-
stub.send(method, path) { |env| [200, {}, remote_response.to_json] }
|
6
|
-
end
|
7
|
-
}
|
8
|
-
let(:connection) { Faraday.new { |f| f.adapter :test, faraday_stubs } }
|
9
|
-
let(:call_args) { { connection: connection } }
|
10
|
-
|
11
|
-
it "calls #{method} method with #{path}" do
|
12
|
-
expect(connection).to receive(method).with(path, any_args).and_call_original
|
13
|
-
call
|
14
|
-
end
|
15
|
-
|
16
|
-
context "with no connection passed" do
|
17
|
-
let(:call_args) { {} }
|
18
|
-
|
19
|
-
it "defaults connection to Tessa.config.connection" do
|
20
|
-
expect(Tessa.config).to receive(:connection).and_return(connection)
|
21
|
-
expect(connection).to receive(method).and_call_original
|
22
|
-
call
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "when response is not successful" do
|
27
|
-
let(:connection) { Tessa::FakeConnection.new }
|
28
|
-
|
29
|
-
it "raises Tessa::RequestFailed" do
|
30
|
-
expect{ call }.to raise_error { |error|
|
31
|
-
expect(error).to be_a(Tessa::RequestFailed)
|
32
|
-
}
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
it "returns an instance of #{return_type}" do
|
37
|
-
expect(call).to be_a(return_type)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Tessa::Asset::Failure do
|
4
|
-
subject(:failure) { described_class.new(args) }
|
5
|
-
let(:id) { rand(100) }
|
6
|
-
let(:args) {
|
7
|
-
{ id: id, message: "Test" }
|
8
|
-
}
|
9
|
-
|
10
|
-
describe "#initialize" do
|
11
|
-
it "sets id to attribute" do
|
12
|
-
expect(failure.id).to eq(args[:id])
|
13
|
-
end
|
14
|
-
|
15
|
-
it "sets message to attribute" do
|
16
|
-
expect(failure.message).to eq(args[:message])
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe ".factory" do
|
21
|
-
let(:message) { 'test message' }
|
22
|
-
let(:response) { double(status: 500) }
|
23
|
-
subject(:failure) { described_class.factory(id: id, response: response) }
|
24
|
-
|
25
|
-
|
26
|
-
it "returns instance of Failure" do
|
27
|
-
expect(described_class).to receive(:message_from_status)
|
28
|
-
.with(response.status).and_return(message)
|
29
|
-
expect(failure.id).to eq(id)
|
30
|
-
expect(failure.message).to eq(message)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
it "responds like a blank asset" do
|
35
|
-
asset = Tessa::Asset.new
|
36
|
-
expect(failure.status).to eq(asset.status)
|
37
|
-
expect(failure.strategy).to eq(asset.strategy)
|
38
|
-
expect(failure.public_url).to eq(asset.public_url)
|
39
|
-
expect(failure.private_url).to eq(asset.private_url)
|
40
|
-
expect(failure.delete_url).to eq(asset.delete_url)
|
41
|
-
end
|
42
|
-
|
43
|
-
describe "#failure?" do
|
44
|
-
it "returns true" do
|
45
|
-
expect(failure).to be_failure
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,198 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Tessa::AssetChangeSet do
|
4
|
-
subject(:set) { described_class.new(args) }
|
5
|
-
let(:args) {
|
6
|
-
{
|
7
|
-
changes: Array.new(2) { Tessa::AssetChange.new },
|
8
|
-
scoped_ids: [1, 2],
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
|
-
describe "#initialize" do
|
13
|
-
it "sets :changes to attribute" do
|
14
|
-
expect(set.changes).to eq(args[:changes])
|
15
|
-
end
|
16
|
-
|
17
|
-
it "sets :scoped_ids to attribute" do
|
18
|
-
expect(set.scoped_ids).to eq([1, 2])
|
19
|
-
end
|
20
|
-
|
21
|
-
context "with various scoped_id input types" do
|
22
|
-
before { args[:scoped_ids] = ["1", 2, nil] }
|
23
|
-
|
24
|
-
it "ensures all values are integers and ignores nils" do
|
25
|
-
expect(set.scoped_ids).to eq([1, 2])
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context "with hash format for changes" do
|
30
|
-
before do
|
31
|
-
args[:changes] = { "123" => { "action" => "add" }, "456" => { "action" => "remove" } }
|
32
|
-
end
|
33
|
-
|
34
|
-
it "initializes AssetChange objects for each hash" do
|
35
|
-
expect(set.changes[0].id).to eq(123)
|
36
|
-
expect(set.changes[0].action).to eq("add")
|
37
|
-
expect(set.changes[1].id).to eq(456)
|
38
|
-
expect(set.changes[1].action).to eq("remove")
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe "#scoped_changes" do
|
44
|
-
context "with changes not contained in scoped ids" do
|
45
|
-
let(:args) {
|
46
|
-
{
|
47
|
-
changes: [
|
48
|
-
Tessa::AssetChange.new(id: 1),
|
49
|
-
Tessa::AssetChange.new(id: 2),
|
50
|
-
Tessa::AssetChange.new(id: 3),
|
51
|
-
Tessa::AssetChange.new(id: 4)],
|
52
|
-
scoped_ids: [1, 2],
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
|
-
it "only returns the scoped changes" do
|
57
|
-
expect(set.scoped_changes.size).to eq(2)
|
58
|
-
expect(set.scoped_changes.map(&:id)).to eq([1, 2])
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe "#apply" do
|
64
|
-
|
65
|
-
context "with unscoped changes" do
|
66
|
-
let(:args) {
|
67
|
-
{
|
68
|
-
changes: [
|
69
|
-
Tessa::AssetChange.new(id: 1),
|
70
|
-
Tessa::AssetChange.new(id: 2)],
|
71
|
-
scoped_ids: [1],
|
72
|
-
}
|
73
|
-
}
|
74
|
-
|
75
|
-
it "calls apply on each scoped change" do
|
76
|
-
expect(set.changes[0]).to receive(:apply)
|
77
|
-
expect(set.changes[1]).not_to receive(:apply)
|
78
|
-
set.apply
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
context "with duplicate changes" do
|
83
|
-
let(:args) {
|
84
|
-
{
|
85
|
-
changes: [
|
86
|
-
Tessa::AssetChange.new(id: 1, action: 'remove'),
|
87
|
-
Tessa::AssetChange.new(id: 1, action: 'remove')],
|
88
|
-
scoped_ids: [1],
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
it "only calls apply on unique elements" do
|
93
|
-
expect(el = set.changes.uniq.first).to receive(:apply)
|
94
|
-
(set.changes - [el]).each { |o| expect(el).to_not receive(:apply) }
|
95
|
-
set.apply
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
describe "#+" do
|
101
|
-
let(:a) {
|
102
|
-
described_class.new(
|
103
|
-
changes: [Tessa::AssetChange.new(id: 1, action: 'add')],
|
104
|
-
scoped_ids: [1],
|
105
|
-
)
|
106
|
-
}
|
107
|
-
let(:b) {
|
108
|
-
described_class.new(
|
109
|
-
changes: [Tessa::AssetChange.new(id: 2, action: 'add')],
|
110
|
-
scoped_ids: [2],
|
111
|
-
)
|
112
|
-
}
|
113
|
-
subject(:sum) { a + b }
|
114
|
-
|
115
|
-
it "concatenates the values of changes" do
|
116
|
-
expect(sum.changes.collect(&:id)).to eq([1, 2])
|
117
|
-
end
|
118
|
-
|
119
|
-
it "concatenates the values of scoped_ids" do
|
120
|
-
expect(sum.scoped_ids).to eq([1, 2])
|
121
|
-
end
|
122
|
-
|
123
|
-
context "with duplicate entries" do
|
124
|
-
subject(:sum) { a + b + b }
|
125
|
-
|
126
|
-
it "only includes unique values" do
|
127
|
-
expect(sum.changes.collect(&:id)).to eq([1, 2])
|
128
|
-
expect(sum.scoped_ids).to eq([1, 2])
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe "#add" do
|
134
|
-
let(:args) { {} }
|
135
|
-
let(:asset) { Tessa::Asset.new(id: 1) }
|
136
|
-
|
137
|
-
shared_examples_for "adds the represented asset" do
|
138
|
-
it "adds an 'add' change for this asset" do
|
139
|
-
expect(set.changes[0].id).to eq(1)
|
140
|
-
expect(set.changes[0].action).to eq("add")
|
141
|
-
end
|
142
|
-
|
143
|
-
it "adds the asset id to the scoped_ids array" do
|
144
|
-
expect(set.scoped_ids).to eq([1])
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
context "when passed an asset" do
|
149
|
-
before do
|
150
|
-
set.add(asset)
|
151
|
-
end
|
152
|
-
|
153
|
-
it_behaves_like "adds the represented asset"
|
154
|
-
end
|
155
|
-
|
156
|
-
context "when passed an integer id" do
|
157
|
-
before do
|
158
|
-
set.add(1)
|
159
|
-
end
|
160
|
-
|
161
|
-
it_behaves_like "adds the represented asset"
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe "#remove" do
|
166
|
-
let(:args) { {} }
|
167
|
-
let(:asset) { Tessa::Asset.new(id: 1) }
|
168
|
-
|
169
|
-
shared_examples_for "removes the represented asset" do
|
170
|
-
it "adds a 'remove' change for this asset" do
|
171
|
-
expect(set.changes[0].id).to eq(1)
|
172
|
-
expect(set.changes[0].action).to eq("remove")
|
173
|
-
end
|
174
|
-
|
175
|
-
it "adds the asset id to the scoped_ids array" do
|
176
|
-
expect(set.scoped_ids).to eq([1])
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
context "when passed an asset" do
|
181
|
-
before do
|
182
|
-
set.remove(asset)
|
183
|
-
end
|
184
|
-
|
185
|
-
it_behaves_like "removes the represented asset"
|
186
|
-
end
|
187
|
-
|
188
|
-
context "when passed an integer id" do
|
189
|
-
before do
|
190
|
-
set.remove(1)
|
191
|
-
end
|
192
|
-
|
193
|
-
it_behaves_like "removes the represented asset"
|
194
|
-
end
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Tessa::AssetChange do
|
4
|
-
subject(:change) { described_class.new(args) }
|
5
|
-
let(:args) {
|
6
|
-
{
|
7
|
-
id: 123,
|
8
|
-
action: "add",
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
|
-
describe "#initialize" do
|
13
|
-
context "with hash args" do
|
14
|
-
it "sets id" do
|
15
|
-
expect(subject.id).to eq(123)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "sets action" do
|
19
|
-
expect(subject.action).to eq("add")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "with array arg" do
|
24
|
-
let(:args) { [123, { "action" => "add" }] }
|
25
|
-
|
26
|
-
it "sets id" do
|
27
|
-
expect(subject.id).to eq(123)
|
28
|
-
end
|
29
|
-
|
30
|
-
it "sets action" do
|
31
|
-
expect(subject.action).to eq("add")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "#apply" do
|
37
|
-
let(:asset) { instance_spy(Tessa::Asset) }
|
38
|
-
before do
|
39
|
-
expect(Tessa::Asset).to receive(:new).with(id: 123).and_return(asset)
|
40
|
-
end
|
41
|
-
|
42
|
-
context "with action 'add'" do
|
43
|
-
before { args[:action] = "add" }
|
44
|
-
it "calls complete! on asset with :id" do
|
45
|
-
change.apply
|
46
|
-
expect(asset).to have_received(:complete!)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context "with action 'remove'" do
|
51
|
-
before { args[:action] = "remove" }
|
52
|
-
it "calls delete! on asset with :id" do
|
53
|
-
change.apply
|
54
|
-
expect(asset).to have_received(:delete!)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "#add?" do
|
60
|
-
subject(:add?) { change.add? }
|
61
|
-
|
62
|
-
context "action = 'add'" do
|
63
|
-
before { change.action = 'add' }
|
64
|
-
it { is_expected.to eq(true) }
|
65
|
-
end
|
66
|
-
|
67
|
-
context "action != 'add'" do
|
68
|
-
before { change.action = 'something' }
|
69
|
-
it { is_expected.to eq(false) }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe "#remove?" do
|
74
|
-
subject(:remove?) { change.remove? }
|
75
|
-
|
76
|
-
context "action = 'remove'" do
|
77
|
-
before { change.action = 'remove' }
|
78
|
-
it { is_expected.to eq(true) }
|
79
|
-
end
|
80
|
-
|
81
|
-
context "action != 'remove'" do
|
82
|
-
before { change.action = 'something' }
|
83
|
-
it { is_expected.to eq(false) }
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|