intermodal 0.0.1 → 0.4.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 +7 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/README +6 -4
- data/Rakefile +6 -0
- data/intermodal.gemspec +51 -0
- data/lib/generators/intermodal_generator.rb +8 -0
- data/lib/intermodal.rb +122 -0
- data/lib/intermodal/api.rb +246 -0
- data/lib/intermodal/api/configuration.rb +40 -0
- data/lib/intermodal/api/railties.rb +21 -0
- data/lib/intermodal/concerns/acceptors/named_resource.rb +12 -0
- data/lib/intermodal/concerns/acceptors/resource.rb +12 -0
- data/lib/intermodal/concerns/controllers/accountability.rb +17 -0
- data/lib/intermodal/concerns/controllers/anonymous.rb +24 -0
- data/lib/intermodal/concerns/controllers/authenticatable.rb +24 -0
- data/lib/intermodal/concerns/controllers/paginated_collection.rb +25 -0
- data/lib/intermodal/concerns/controllers/presentation.rb +17 -0
- data/lib/intermodal/concerns/controllers/resource.rb +51 -0
- data/lib/intermodal/concerns/controllers/resource_linking.rb +58 -0
- data/lib/intermodal/concerns/let.rb +21 -0
- data/lib/intermodal/concerns/models/access_credential.rb +28 -0
- data/lib/intermodal/concerns/models/account.rb +16 -0
- data/lib/intermodal/concerns/models/accountability.rb +47 -0
- data/lib/intermodal/concerns/models/db_access_token.rb +29 -0
- data/lib/intermodal/concerns/models/has_parent_resource.rb +45 -0
- data/lib/intermodal/concerns/models/presentation.rb +25 -0
- data/lib/intermodal/concerns/models/redis_access_token.rb +60 -0
- data/lib/intermodal/concerns/models/resource_linking.rb +126 -0
- data/lib/intermodal/concerns/models/sanitize_html.rb +35 -0
- data/lib/intermodal/concerns/presenters/named_resource.rb +12 -0
- data/lib/intermodal/concerns/presenters/resource.rb +14 -0
- data/lib/intermodal/concerns/rails/rails_3_stack.rb +42 -0
- data/lib/intermodal/concerns/rails/rails_4_stack.rb +17 -0
- data/lib/intermodal/concerns/rails/use_warden.rb +21 -0
- data/lib/intermodal/config.rb +15 -0
- data/lib/intermodal/configuration.rb +11 -0
- data/lib/intermodal/controllers/api_controller.rb +26 -0
- data/lib/intermodal/controllers/linking_resource_controller.rb +8 -0
- data/lib/intermodal/controllers/nested_resource_controller.rb +18 -0
- data/lib/intermodal/controllers/resource_controller.rb +11 -0
- data/lib/intermodal/dsl/controllers.rb +125 -0
- data/lib/intermodal/dsl/mapping.rb +79 -0
- data/lib/intermodal/dsl/presentation_helpers.rb +107 -0
- data/lib/intermodal/mapping/acceptor.rb +2 -2
- data/lib/intermodal/mapping/mapper.rb +39 -13
- data/lib/intermodal/mapping/presenter.rb +12 -6
- data/lib/intermodal/proxies/linking_resources.rb +58 -0
- data/lib/intermodal/proxies/will_paginate.rb +85 -0
- data/lib/intermodal/rack/auth.rb +29 -0
- data/lib/intermodal/rack/dummy_store.rb +24 -0
- data/lib/intermodal/rack/rescue.rb +82 -0
- data/lib/intermodal/responders/linking_resource_responder.rb +21 -0
- data/lib/intermodal/responders/resource_responder.rb +64 -0
- data/lib/intermodal/rspec/acceptors.rb +79 -0
- data/lib/intermodal/rspec/models/accountability.rb +114 -0
- data/lib/intermodal/rspec/models/has_parent_resource.rb +132 -0
- data/lib/intermodal/rspec/models/resource_linking.rb +234 -0
- data/lib/intermodal/rspec/models/sanitization.rb +84 -0
- data/lib/intermodal/rspec/presenters.rb +92 -0
- data/lib/intermodal/rspec/requests/authenticated_requests.rb +17 -0
- data/lib/intermodal/rspec/requests/linked_resources.rb +180 -0
- data/lib/intermodal/rspec/requests/paginated_collection.rb +60 -0
- data/lib/intermodal/rspec/requests/rack.rb +142 -0
- data/lib/intermodal/rspec/requests/request_validations.rb +36 -0
- data/lib/intermodal/rspec/requests/resources.rb +275 -0
- data/lib/intermodal/rspec/requests/rfc2616_status_codes.rb +51 -0
- data/lib/intermodal/rspec/validators.rb +86 -0
- data/lib/intermodal/validators/account_validator.rb +27 -0
- data/lib/intermodal/validators/different_account_validator.rb +27 -0
- data/lib/intermodal/version.rb +3 -0
- data/spec/mapping/acceptors_spec.rb +142 -0
- data/spec/mapping/presenters_spec.rb +186 -0
- data/spec/models/accountability_spec.rb +13 -0
- data/spec/models/has_parent_resource_spec.rb +18 -0
- data/spec/models/resource_linking_spec.rb +21 -0
- data/spec/proxies/will_paginate_spec.rb +163 -0
- data/spec/rack/auth_spec.rb +51 -0
- data/spec/requests/linked_resources.rb +37 -0
- data/spec/requests/nested_resources_spec.rb +54 -0
- data/spec/requests/resources_spec.rb +50 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/api.rb +50 -0
- data/spec/support/app/class_builder.rb +41 -0
- data/spec/support/app/db/adapter_helper.rb +53 -0
- data/spec/support/app/db/authentication_schema_helper.rb +62 -0
- data/spec/support/app/db/migration_helper.rb +44 -0
- data/spec/support/app/schema.rb +101 -0
- data/spec/support/application.rb +23 -0
- data/spec/support/blueprints.rb +41 -0
- data/spec/support/epiphyte.rb +29 -0
- metadata +393 -52
- data/lib/intermodal/base.rb +0 -13
- data/lib/intermodal/declare_controllers.rb +0 -102
- data/lib/intermodal/mapping.rb +0 -4
- data/lib/intermodal/mapping/dsl.rb +0 -76
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module RSpec
|
|
3
|
+
# All Intermodal controllers require models that belongs to an
|
|
4
|
+
# account object and responds to #get. This allows the controllers
|
|
5
|
+
# to fetch records authorized by the account.
|
|
6
|
+
#
|
|
7
|
+
# This RSpec macro tests these assumptions.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# class Resource < ActiveRecord::Base
|
|
12
|
+
# include Intermodal::Models::Accountability
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# describe Resource do
|
|
16
|
+
# include Intermodal::RSpec::Accountability
|
|
17
|
+
#
|
|
18
|
+
# concerned_with_accountability
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# It works with Remarkable. It might work with Shoulda
|
|
22
|
+
#
|
|
23
|
+
# If you don't want to use either, you can try
|
|
24
|
+
#
|
|
25
|
+
# describe Resource do
|
|
26
|
+
# include Intermodal::RSpec::Accountability
|
|
27
|
+
#
|
|
28
|
+
# implements_get_interface
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
module Accountability
|
|
32
|
+
extend ActiveSupport::Concern
|
|
33
|
+
|
|
34
|
+
module ClassMethods
|
|
35
|
+
def concerned_with_accountability(&blk)
|
|
36
|
+
instance_eval(&blk) if blk
|
|
37
|
+
|
|
38
|
+
context 'when concerned with accountability' do
|
|
39
|
+
let(:model) { subject.class }
|
|
40
|
+
it { should belong_to :account }
|
|
41
|
+
it { should validate_presence_of :account_id }
|
|
42
|
+
it { model.should respond_to :by_account_id }
|
|
43
|
+
it { model.should respond_to :by_account }
|
|
44
|
+
|
|
45
|
+
implements_get_interface
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def implements_get_interface(&blk)
|
|
50
|
+
describe '.get' do
|
|
51
|
+
let(:model) { subject.class }
|
|
52
|
+
let(:different_account) { Account.make! }
|
|
53
|
+
|
|
54
|
+
it { model.should respond_to :get }
|
|
55
|
+
|
|
56
|
+
context ':all' do
|
|
57
|
+
context 'when unscoped to account' do
|
|
58
|
+
let(:collection) { model.get(:all) }
|
|
59
|
+
it 'should find all resources' do
|
|
60
|
+
collection.should include(resource)
|
|
61
|
+
collection.size.should eql(1)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'when scoped to account' do
|
|
66
|
+
let(:collection) { model.get(:all, account: account) }
|
|
67
|
+
it 'should find all resources' do
|
|
68
|
+
collection.should include(resource)
|
|
69
|
+
collection.size.should eql(1)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'by id' do
|
|
75
|
+
it 'should find resource by id' do
|
|
76
|
+
model.get(resource.id).should eql(resource)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'should return a writeable resource' do
|
|
80
|
+
model.get(resource.id).should_not be_readonly
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context 'by account' do
|
|
85
|
+
it 'should find resource scoped to account' do
|
|
86
|
+
model.get(resource.id, account: account).should eql(resource)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'should find resource scoped to account id' do
|
|
90
|
+
model.get(resource.id, account_id: account.id).should eql(resource)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'should find a writeable resource scoped to account id' do
|
|
94
|
+
model.get(resource.id, account_id: account.id).should_not be_readonly
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'should not find resource scoped to a different account' do
|
|
98
|
+
expect { model.get(resource.id, account: different_account) }.to raise_error(ActiveRecord::RecordNotFound)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'should not find resource scoped to a different account id ' do
|
|
102
|
+
expect { model.get(resource.id, account_id: different_account.id) }.to raise_error(ActiveRecord::RecordNotFound)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
instance_eval(&blk) if blk
|
|
107
|
+
end
|
|
108
|
+
end # implements_get_interface
|
|
109
|
+
end # ClassMethods
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module RSpec
|
|
3
|
+
# All Intermodal nested resource controllers require models that
|
|
4
|
+
# belongs to an parent object that belongs to an account. It should
|
|
5
|
+
# also respond to #get and verify authorization for the account.
|
|
6
|
+
#
|
|
7
|
+
# This RSpec macro tests these assumptions.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# class ParentResource < ActiveRecord::Base
|
|
12
|
+
# include Intermodal::Models::Accountability
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# class NestedResource < ActiveRecord::Base
|
|
16
|
+
# include Intermodal::Models::HasParentResources
|
|
17
|
+
#
|
|
18
|
+
# parent_resource :parent_resource
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# describe NestedResource do
|
|
22
|
+
# include Intermodal::RSpec::HasParentResource
|
|
23
|
+
#
|
|
24
|
+
# concerned_with_parent_resource :parent_resource
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# It works with Remarkable. It might work with Shoulda
|
|
28
|
+
#
|
|
29
|
+
# If you don't want to use either, you can pass:
|
|
30
|
+
#
|
|
31
|
+
# describe NestedResource do
|
|
32
|
+
# include Intermodal::RSpec::HasParentResource
|
|
33
|
+
#
|
|
34
|
+
# let(:parent_resource_name) { :parent_resource }
|
|
35
|
+
# let(:different_parent) { ParentResource.make!(:account => account) }
|
|
36
|
+
# let(:parent_with_different_account) { ParentResource.make!(:account => different_account) }
|
|
37
|
+
# let(:different_account) { Account.make! }
|
|
38
|
+
#
|
|
39
|
+
# implements_get_interface_for_nested_resource
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
module HasParentResource
|
|
43
|
+
extend ActiveSupport::Concern
|
|
44
|
+
|
|
45
|
+
included do
|
|
46
|
+
include Intermodal::RSpec::Accountability
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module ClassMethods
|
|
50
|
+
|
|
51
|
+
# NOTE: I am cheating. It assumes if the class method is defined then
|
|
52
|
+
# it is a scope
|
|
53
|
+
def should_respond_to_scope(scope_method)
|
|
54
|
+
it "should have scope #{scope_method}" do
|
|
55
|
+
subject.class.should respond_to(scope_method)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def concerned_with_parent_resource(_parent_resource_name, options = {}, &blk)
|
|
60
|
+
extra_get_examples = options[:extra_get_examples]
|
|
61
|
+
|
|
62
|
+
context "when concerned with parent resource #{_parent_resource_name}" do
|
|
63
|
+
let(:parent_resource_name) { _parent_resource_name }
|
|
64
|
+
let(:parent_id_name) { "#{parent_resource_name}_id" }
|
|
65
|
+
let(:parent_model) { parent_resource_name.to_s.camelize.constantize }
|
|
66
|
+
let(:different_parent) { parent_model.make! }
|
|
67
|
+
let(:parent_with_different_account) { parent_model.make!(:account => different_account) }
|
|
68
|
+
|
|
69
|
+
instance_eval(&blk) if blk
|
|
70
|
+
|
|
71
|
+
it { should belong_to _parent_resource_name } unless options[:skip_association_examples]
|
|
72
|
+
it { should validate_presence_of _parent_resource_name } unless options[:skip_validation_examples]
|
|
73
|
+
|
|
74
|
+
[ :by_parent_id, :by_parent, "by_#{_parent_resource_name}_id", "by_#{_parent_resource_name}" ].each do |scope|
|
|
75
|
+
should_respond_to_scope(scope)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
implements_get_interface_for_nested_resource(&extra_get_examples)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def implements_get_interface_for_nested_resource(&blk)
|
|
83
|
+
implements_get_interface do
|
|
84
|
+
|
|
85
|
+
context 'by parent' do
|
|
86
|
+
it 'should find resource scoped to parent' do
|
|
87
|
+
model.get(subject.id, :parent => subject.send(parent_resource_name)).should eql(subject)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'should find resource scoped to parent id' do
|
|
91
|
+
model.get(subject.id, :parent_id => subject.send(parent_resource_name).id).should eql(subject)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'should find writeable resource scoped to parent id' do
|
|
95
|
+
model.get(subject.id, :parent_id => subject.send(parent_resource_name).id).should_not be_readonly
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context 'by parent and account' do
|
|
100
|
+
it 'should find resource scoped to account id and parent id' do
|
|
101
|
+
model.get(subject.id, :parent_id => subject.send(parent_resource_name).id, :account_id => account.id).should eql(subject)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'should find writeable resource scoped to account id and parent id' do
|
|
105
|
+
model.get(subject.id, :parent_id => subject.send(parent_resource_name).id, :account_id => account.id).should_not be_readonly
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'should not find resource scoped to a different parent' do
|
|
109
|
+
lambda { model.get(subject.id, :parent => different_parent) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'should not find resource scoped to a different parent id' do
|
|
113
|
+
lambda { model.get(subject.id, :parent_id => different_parent.id) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should not find resource scoped to a parent in a different account' do
|
|
117
|
+
lambda { model.get(subject.id, :parent_id => parent_with_different_account.id, :account_id => account) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'should not find resource scoped to a correct parent but incorrect account' do
|
|
121
|
+
lambda { model.get(subject.id, :parent_id => subject.send(parent_resource_name).id, :account_id => different_account) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
instance_eval(&blk) if blk
|
|
126
|
+
end
|
|
127
|
+
end # implements_get_interface_for_nested_resource
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module RSpec
|
|
3
|
+
module ResourceLinking
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
include Intermodal::RSpec::HasParentResource
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
# To use this, you must define the resources in SpecHelpers::Application
|
|
12
|
+
# This ensures that all the linked resources are scoped to the same account
|
|
13
|
+
#
|
|
14
|
+
# Example:
|
|
15
|
+
#
|
|
16
|
+
# concerned_with_resource_linking :item, :vendor
|
|
17
|
+
#
|
|
18
|
+
# Somewhere within the spec scope, you must define the following:
|
|
19
|
+
#
|
|
20
|
+
# let(:model) { Sku }
|
|
21
|
+
# let(:skus) { item.vendors << vendors }
|
|
22
|
+
# let(:item) { Item.make(:account => account) }
|
|
23
|
+
# let(:vendors) { (1..3).map { Vendor.make(:account => account) }
|
|
24
|
+
# let(:account) { Account.make }
|
|
25
|
+
#
|
|
26
|
+
# Optional:
|
|
27
|
+
# If you are not running Remarkable or Shoulda, you can pass :skip_association_examples => true
|
|
28
|
+
def concerned_with_resource_linking(_parent_resource_name, _target_resource_name, options = {}, &blk)
|
|
29
|
+
context "when concerned with linking #{_parent_resource_name} to #{_target_resource_name}", options do
|
|
30
|
+
_metadata = metadata
|
|
31
|
+
|
|
32
|
+
let(:parent_resource_name) { _parent_resource_name }
|
|
33
|
+
let(:target_resource_name) { _target_resource_name }
|
|
34
|
+
|
|
35
|
+
let(:model_name) { model.model_name.demodulize.underscore.to_sym }
|
|
36
|
+
let(:model_association_name) { model_name.to_s.pluralize }
|
|
37
|
+
let(:parent) { send(parent_resource_name) }
|
|
38
|
+
let(:parent_with_different_account) { parent.class.make(:account => different_account) }
|
|
39
|
+
let(:targets) { send(target_resource_name.to_s.pluralize) }
|
|
40
|
+
let(:list) { send(model_association_name) }
|
|
41
|
+
|
|
42
|
+
let(:target_association_name) { target_resource_name.to_s.pluralize }
|
|
43
|
+
let(:target_model) { (_metadata[:class_name] || target_resource_name).to_s.camelize.constantize }
|
|
44
|
+
let(:target_model_blueprint) { proc do target_model.make!(:account => account) end }
|
|
45
|
+
let(:target_model_blueprint_with_different_account) { proc do target_model.make!(:account => different_account) end }
|
|
46
|
+
let(:target_accounts) { targets.map(&:account) }
|
|
47
|
+
let(:target_with_different_account) { target_model_blueprint_with_different_account.call() }
|
|
48
|
+
let(:targets_with_different_account) { (1..3).map { target_model_blueprint_with_different_account.call() } }
|
|
49
|
+
let(:target_ids_with_different_account) { targets_with_different_account.map(&:id) }
|
|
50
|
+
|
|
51
|
+
instance_eval(&blk) if blk
|
|
52
|
+
|
|
53
|
+
# Examples
|
|
54
|
+
get_examples_for_linked_targets = proc do
|
|
55
|
+
pending 'by target' do
|
|
56
|
+
it 'should find targets scoped to the same account' do
|
|
57
|
+
target_accounts.each do |target_account|
|
|
58
|
+
target_account.should eql(parent_account)
|
|
59
|
+
end
|
|
60
|
+
model.get(subject.id, :account => parent_account).should eql(subject)
|
|
61
|
+
end
|
|
62
|
+
it 'should not find targets scoped to another account' do
|
|
63
|
+
parent.send(target_association_name) << target_with_different_account
|
|
64
|
+
model.get(:all, :account => different_account).send("to_#{target_resource}_ids").should_not include(target_with_different_account.id)
|
|
65
|
+
end
|
|
66
|
+
let(:parent_association) {
|
|
67
|
+
parent.send(target_association_name) << target_with_different_account
|
|
68
|
+
parent.send(target_association_name) }
|
|
69
|
+
let(:query) { model.get(:all, :account => different_account).to_sql }
|
|
70
|
+
#debug_examples :target_with_different_account, :parent_association, :account, :different_account, :parent, :query
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
concerned_with_parent_resource(_parent_resource_name, { :extra_get_examples => get_examples_for_linked_targets }.merge(options)) do
|
|
75
|
+
let(:parent_model) { parent.class }
|
|
76
|
+
let(:parent_account) { parent.account }
|
|
77
|
+
instance_eval(&options[:extra_parent_resource_examples]) if options[:extra_parent_resource_examples]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless options[:skip_association_examples]
|
|
81
|
+
it { should belong_to target_resource_name }
|
|
82
|
+
it { subject.class.should respond_to :by_target_id }
|
|
83
|
+
it { subject.class.should respond_to "by_#{target_resource_name}_id" }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe ".to_#{_target_resource_name}_ids" do
|
|
87
|
+
subject { list; parent.send(model_association_name).send("to_#{_target_resource_name}_ids") }
|
|
88
|
+
|
|
89
|
+
it "should return a list of #{_target_resource_name} ids" do
|
|
90
|
+
should be_kind_of(Array)
|
|
91
|
+
subject.map do |id|
|
|
92
|
+
(id + 0).should eql(id)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should return all #{_target_resource_name} ids that linked to the #{_parent_resource_name}" do
|
|
97
|
+
target_model.where(:id => subject).each do |linked_resource|
|
|
98
|
+
targets.should include(linked_resource)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe '.replace' do
|
|
104
|
+
let(:replacement_targets) { (1..3).map { target_model_blueprint.call() } }
|
|
105
|
+
let(:replacement_target_ids) { replacement_targets.map(&:id) }
|
|
106
|
+
let(:original_target_ids) { list; parent.send(target_association_name).map(&:id) }
|
|
107
|
+
let(:updated_target_ids) { model.get(:all, :parent => parent).to_target_ids }
|
|
108
|
+
subject { list; model.replace(parent.id, replacement_target_ids) }
|
|
109
|
+
|
|
110
|
+
it "should link the target ids to the parent #{_parent_resource_name}" do
|
|
111
|
+
original_target_ids.should_not be_empty
|
|
112
|
+
subject.should_not be_empty
|
|
113
|
+
updated_target_ids.sort.should eql(replacement_target_ids.sort)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context 'when scoped to an account' do
|
|
117
|
+
it 'should link to a parent scoped to the account' do
|
|
118
|
+
model.replace(parent, replacement_target_ids, :account => account).should_not be_empty
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'should not link to a parent scoped to a different account' do
|
|
122
|
+
lambda { model.replace(parent_with_different_account.id, replacement_target_ids, :account => account) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should only accept target ids that belong to the same account" do
|
|
126
|
+
subject.should_not be_empty
|
|
127
|
+
model.replace(parent.id, target_ids_with_different_account, :account => account).should be_empty
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'should only accept target ids that belong to the same account id' do
|
|
131
|
+
subject.should_not be_empty
|
|
132
|
+
model.replace(parent.id, target_ids_with_different_account, :account_id => account.id).should be_empty
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "should delete the existing links to the parent #{_parent_resource_name}" do
|
|
137
|
+
original_target_ids.should_not be_empty
|
|
138
|
+
subject.should_not be_empty
|
|
139
|
+
(original_target_ids & updated_target_ids).should be_empty
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe '.append' do
|
|
144
|
+
let(:additional_targets) { (1..3).map { target_model_blueprint.call() } }
|
|
145
|
+
let(:additional_target_ids) { additional_targets.map(&:id) }
|
|
146
|
+
let(:original_target_ids) { list; parent.send(target_association_name).map(&:id) }
|
|
147
|
+
let(:updated_target_ids) { parent.send(target_association_name).map(&:id) }
|
|
148
|
+
subject { list; model.append(parent.id, additional_target_ids) }
|
|
149
|
+
|
|
150
|
+
it "should link additional #{_target_resource_name} ids to the parent #{_parent_resource_name}" do
|
|
151
|
+
subject.should_not be_empty
|
|
152
|
+
additional_target_ids do |additional_target_id|
|
|
153
|
+
updated_target_ids.should include(additional_target_id)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
context 'when scoped to an account' do
|
|
158
|
+
it 'should link to a parent scoped to the account' do
|
|
159
|
+
model.append(parent, additional_target_ids, :account => account).should_not be_empty
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'should not link to a parent scoped to a different account' do
|
|
163
|
+
lambda { model.append(parent_with_different_account.id, additional_target_ids, :account => account) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should only accept target ids that belong to the same account" do
|
|
167
|
+
subject.should_not be_empty
|
|
168
|
+
model.append(parent.id, target_ids_with_different_account, :account => account).should_not be_empty
|
|
169
|
+
updated_target_ids.should eql(updated_target_ids - target_ids_with_different_account)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'should only accept target ids that belong to the same account id' do
|
|
173
|
+
subject.should_not be_empty
|
|
174
|
+
model.append(parent.id, target_ids_with_different_account, :account_id => account.id).should_not be_empty
|
|
175
|
+
updated_target_ids.should eql(updated_target_ids - target_ids_with_different_account)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "should keep existing links to the parent #{_parent_resource_name}" do
|
|
180
|
+
original_target_ids.should_not be_empty
|
|
181
|
+
subject.should_not be_empty
|
|
182
|
+
original_target_ids do |original_target_id|
|
|
183
|
+
updated_target_ids.should include(original_target_id)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "should not append existing links to the parent #{_parent_resource_name}" do
|
|
188
|
+
2.times { model.append(parent.id, additional_target_ids) }
|
|
189
|
+
additional_target_ids.each do |additional_target_id|
|
|
190
|
+
model.by_parent(parent).by_target_id(additional_target_id).length.should eql(1)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
describe '.remove' do
|
|
196
|
+
let(:original_target_ids) { list; parent.send(target_association_name).map(&:id) }
|
|
197
|
+
let(:removed_target_ids) { original_target_ids[0..1] }
|
|
198
|
+
let(:remaining_target_ids) { original_target_ids - removed_target_ids }
|
|
199
|
+
let(:updated_target_ids) { parent.send(target_association_name).map(&:id) }
|
|
200
|
+
subject { list; model.remove(parent.id, removed_target_ids) }
|
|
201
|
+
|
|
202
|
+
it "should delete the existing links to the parent #{_parent_resource_name}" do
|
|
203
|
+
removed_target_ids.should_not be_empty
|
|
204
|
+
subject.should_not be_empty
|
|
205
|
+
removed_target_ids do |removed_target_id|
|
|
206
|
+
updated_target_ids.should_not include(removed_target_id)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
context 'when scoped to an account' do
|
|
211
|
+
it 'should link to a parent scoped to the account' do
|
|
212
|
+
model.remove(parent, removed_target_ids, :account => account).should_not be_empty
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'should not unlink from a parent scoped to a different account' do
|
|
216
|
+
lambda { model.remove(parent_with_different_account.id, removed_target_ids, :account => account) }.should raise_error(ActiveRecord::RecordNotFound)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "should keep remaining links to the parent" do
|
|
221
|
+
removed_target_ids.should_not be_empty
|
|
222
|
+
subject.should_not be_empty
|
|
223
|
+
remaining_target_ids do |original_target_id|
|
|
224
|
+
updated_target_ids.should include(remaining_target_id)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|