tessa 2.0 → 6.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -4
- 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
|