party_resource 0.0.1
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.
- data/LICENSE +20 -0
- data/README.rdoc +19 -0
- data/lib/party_resource/buildable.rb +60 -0
- data/lib/party_resource/connector/base.rb +35 -0
- data/lib/party_resource/connector.rb +42 -0
- data/lib/party_resource/exceptions.rb +57 -0
- data/lib/party_resource/method_define.rb +11 -0
- data/lib/party_resource/party_resource.rb +91 -0
- data/lib/party_resource/property.rb +53 -0
- data/lib/party_resource/request.rb +36 -0
- data/lib/party_resource/route.rb +77 -0
- data/lib/party_resource.rb +8 -0
- data/spec/fixtures/other_class.rb +19 -0
- data/spec/fixtures/test_base_class.rb +17 -0
- data/spec/fixtures/test_class.rb +44 -0
- data/spec/integration/integration_spec.rb +173 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/connector/base_spec.rb +83 -0
- data/spec/unit/connector_spec.rb +75 -0
- data/spec/unit/exceptions_spec.rb +34 -0
- data/spec/unit/party_resource_spec.rb +227 -0
- data/spec/unit/request_spec.rb +103 -0
- data/spec/unit/route_spec.rb +118 -0
- metadata +150 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
require File.expand_path(File.join(__FILE__, '..', '..', 'spec_helper'))
|
2
|
+
require 'fixtures/test_base_class'
|
3
|
+
require 'fixtures/other_class'
|
4
|
+
require 'fixtures/test_class'
|
5
|
+
|
6
|
+
describe TestClass do
|
7
|
+
|
8
|
+
let(:object) {TestClass.new(:foo)}
|
9
|
+
|
10
|
+
describe 'class level call' do
|
11
|
+
it 'raises an argument error when called with the wrong number of arguments' do
|
12
|
+
lambda { TestClass.find }.should raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'finds an object' do
|
16
|
+
stub_request(:get, "http://fred:pass@myserver/path/find/99.ext").to_return(:body => 'some data')
|
17
|
+
TestClass.find(99).should == TestClass.new('some data')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'finds an object with extra options' do
|
21
|
+
stub_request(:get, "http://fred:pass@myserver/path/find/99.ext?extra=options").to_return(:body => 'some data')
|
22
|
+
TestClass.find(99, :extra => 'options').should == TestClass.new('some data')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'uses other connectors' do
|
26
|
+
stub_request(:get, 'http://otherserver/url').to_return(:body => 'from the otherserver')
|
27
|
+
OtherPartyClass.test.should == 'from the otherserver'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'instance level call' do
|
32
|
+
it 'gets the result' do
|
33
|
+
stub_request(:put, "http://fred:pass@myserver/path/update/99.ext").to_return(:body => 'updated data')
|
34
|
+
TestClass.new(:var => 99).update
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'building connected objects' do
|
39
|
+
it 'build the requested response object' do
|
40
|
+
stub_request(:put, "http://fred:pass@myserver/path/update/99.ext").to_return(:body => 'updated data')
|
41
|
+
TestClass.new(:var => 99).update.should == OtherClass.new('updated data')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'passes the raw result back when requested' do
|
45
|
+
stub_request(:post, "http://fred:pass@myserver/path/save/file").with(:body => "data=somedata").to_return(:body => 'saved data')
|
46
|
+
TestClass.save('somedata').should == 'saved data'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'builds the result using the specified method' do
|
50
|
+
stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:body => 'deleted OK')
|
51
|
+
TestClass.destroy.should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'builds the result using the specified proc' do
|
55
|
+
stub_request(:get, "http://fred:pass@myserver/path/foo?value=908").to_return(:body => 'foo data')
|
56
|
+
TestClass.foo(908).should == 'New foo data Improved'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'builds each value in an array individually' do
|
60
|
+
stub_request(:get, "http://fred:pass@myserver/path/foo?value=908").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '[foo,data]')
|
61
|
+
TestClass.foo(908).should == ['New foo Improved', 'New data Improved']
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'passes "included" variables to the new object' do
|
65
|
+
v2 = mock(:v2)
|
66
|
+
stub_request(:get, "http://fred:pass@myserver/path/include").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{}')
|
67
|
+
test = TestClass.from_json(:value2 => v2)
|
68
|
+
test.include.should == OtherClass.new(:thing => v2)
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'error cases' do
|
72
|
+
it 'raises ResourceNotFound' do
|
73
|
+
stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 404)
|
74
|
+
lambda { TestClass.destroy }.should raise_error(PartyResource::ResourceNotFound)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'raises ResourceInvalid' do
|
78
|
+
stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 422)
|
79
|
+
lambda { TestClass.destroy }.should raise_error(PartyResource::ResourceInvalid)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'raises ClientError' do
|
83
|
+
stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 405)
|
84
|
+
lambda { TestClass.destroy }.should raise_error(PartyResource::ClientError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'raises ServerError' do
|
88
|
+
stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 501)
|
89
|
+
lambda { TestClass.destroy }.should raise_error(PartyResource::ServerError)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'rescues exceptions' do
|
93
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:status => 404)
|
94
|
+
TestClass.fetch_json.should be_nil
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'populating properties' do
|
101
|
+
it 'populates result values' do
|
102
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{value2:"value2", value4:"ignored"}')
|
103
|
+
result = TestClass.fetch_json
|
104
|
+
result.value2.should == 'value2'
|
105
|
+
result.value3.should == nil
|
106
|
+
result.should_not be_respond_to(:value4)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'populates renamed values' do
|
110
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{input_name:"value"}')
|
111
|
+
result = TestClass.fetch_json
|
112
|
+
result.value.should == 'value'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'populates nested values' do
|
116
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{block:{var:"value"}}')
|
117
|
+
result = TestClass.fetch_json
|
118
|
+
result.nested_value.should == 'value'
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'falls back to populating based on the property name if from is not found' do
|
122
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{value:"value"}')
|
123
|
+
result = TestClass.fetch_json
|
124
|
+
result.value.should == 'value'
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'populates a property as another class' do
|
128
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{other: "value"}')
|
129
|
+
result = TestClass.fetch_json
|
130
|
+
result.other.should == OtherClass.new('value')
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'populates a property with a proc' do
|
134
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{processed: "value"}')
|
135
|
+
result = TestClass.fetch_json
|
136
|
+
result.processed.should == "Processed: value"
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'does not build null data' do
|
140
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{processed: null}')
|
141
|
+
result = TestClass.fetch_json
|
142
|
+
result.processed.should == nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'builds each value when populating an array' do
|
146
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{processed: [1,2,3,4]}')
|
147
|
+
result = TestClass.fetch_json
|
148
|
+
result.processed.should == ['Processed: 1','Processed: 2','Processed: 3','Processed: 4']
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'and inherited class' do
|
152
|
+
it 'self refers to the child class' do
|
153
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{value2:"value2", child_property:"child"}')
|
154
|
+
result = InheritedTestClass.fetch_json
|
155
|
+
result.should be_a(InheritedTestClass)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'all local and inherited properties are available' do
|
159
|
+
stub_request(:get, "http://fred:pass@myserver/path/big_data").to_return(:headers => {'Content-Type' => 'text/json'}, :body => '{value2:"value2", child_property:"child"}')
|
160
|
+
result = InheritedTestClass.fetch_json
|
161
|
+
result.child_property.should == 'child'
|
162
|
+
result.value2.should == 'value2'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#to_property_hash' do
|
168
|
+
it 'converts an object to external hash representation' do
|
169
|
+
obj = TestClass.from_json(:value => 'v1', :value2 => 'v2', :nested_value => 'nv', :processed => 'Milk', :child => {:thing => 'Happiness'})
|
170
|
+
obj.to_properties_hash.should == {:input_name => 'v1', :value2 => 'v2', :block => {:var => 'nv'}, :output_name => 'Processed: Milk', :child => {:thing => 'Happiness'}}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'party_resource'
|
5
|
+
require 'spec'
|
6
|
+
require 'spec/autorun'
|
7
|
+
require 'webmock/rspec'
|
8
|
+
|
9
|
+
include WebMock
|
10
|
+
|
11
|
+
module LetMock
|
12
|
+
def let_mock(name, options = {})
|
13
|
+
let(name) { mock(name, options) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Spec::Runner.configure do |config|
|
18
|
+
config.extend(LetMock)
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.expand_path(File.join(__FILE__, '..', '..', '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe PartyResource::Connector::Base do
|
4
|
+
describe "creation" do
|
5
|
+
subject { PartyResource::Connector::Base.new(:test, options) }
|
6
|
+
|
7
|
+
let_mock(:username)
|
8
|
+
let_mock(:password)
|
9
|
+
let_mock(:normalized_uri)
|
10
|
+
let_mock(:original_uri)
|
11
|
+
|
12
|
+
before do
|
13
|
+
HTTParty.stub(:normalize_base_uri => normalized_uri)
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with a base_uri" do
|
17
|
+
let(:options) { { :base_uri => original_uri } }
|
18
|
+
|
19
|
+
it 'normalizes the base_uri' do
|
20
|
+
HTTParty.should_receive(:normalize_base_uri).with(original_uri)
|
21
|
+
subject.options.should == {:base_uri => normalized_uri }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with username' do
|
26
|
+
let(:options) { { :username => username } }
|
27
|
+
|
28
|
+
it 'stores the username' do
|
29
|
+
subject.options.should == {:basic_auth => {:username => username, :password => nil} }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with password' do
|
34
|
+
let(:options) { { :password => password } }
|
35
|
+
|
36
|
+
it 'stores the password' do
|
37
|
+
subject.options.should == {:basic_auth => {:password => password, :username => nil} }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with all options' do
|
42
|
+
let(:options) { { :base_uri => original_uri, :username => username, :password => password } }
|
43
|
+
|
44
|
+
it 'stores the options' do
|
45
|
+
subject.options.should == {:base_uri => normalized_uri,
|
46
|
+
:basic_auth => {:password => password, :username => username } }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#fetch' do
|
52
|
+
let(:options) { {:base_uri => 'http://myserver.test/path'} }
|
53
|
+
let_mock(:data, :empty? => false)
|
54
|
+
let_mock(:path)
|
55
|
+
let_mock(:return_data, :code => 200)
|
56
|
+
let(:request) { mock(:request, :path => path, :verb => verb, :http_data => data) }
|
57
|
+
|
58
|
+
subject { PartyResource::Connector::Base.new(:test, options) }
|
59
|
+
|
60
|
+
[:put, :post, :delete, :get].each do |http_verb|
|
61
|
+
context "for #{http_verb} requests" do
|
62
|
+
let(:verb) { http_verb }
|
63
|
+
it "fetches the request using HTTParty" do
|
64
|
+
HTTParty.should_receive(verb).with(path, data).and_return(return_data)
|
65
|
+
subject.fetch(request).should == return_data
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'error cases' do
|
72
|
+
let(:request) { mock(:request, :path => mock(:path), :verb => :get, :http_data => mock(:data)) }
|
73
|
+
|
74
|
+
it 'raises an exception' do
|
75
|
+
return_data = mock(:data, :code => 404)
|
76
|
+
HTTParty.should_receive(:get).and_return(return_data)
|
77
|
+
lambda{ subject.fetch(request) }.should raise_error(PartyResource::ConnectionError)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path(File.join(__FILE__, '..', '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe PartyResource::Connector do
|
4
|
+
describe '#lookup' do
|
5
|
+
|
6
|
+
let(:test_connector) { mock(:connector) }
|
7
|
+
let(:default_connector) { mock(:connector) }
|
8
|
+
let(:connectors) { { :test => test_connector, :other => default_connector } }
|
9
|
+
|
10
|
+
before do
|
11
|
+
PartyResource::Connector.repository.stub(:connectors => connectors)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns the named connectors" do
|
15
|
+
PartyResource::Connector.lookup(:test).should == test_connector
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns the default connector for nil name" do
|
19
|
+
PartyResource::Connector.repository.stub(:default => :other)
|
20
|
+
|
21
|
+
PartyResource::Connector.lookup(nil).should == default_connector
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'raises a NoConnector error it the connector could not be found' do
|
25
|
+
lambda { PartyResource::Connector.lookup(:missing_name) }.should raise_error(PartyResource::NoConnector)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#default=' do
|
30
|
+
it 'sets the default connector name' do
|
31
|
+
name = mock(:name)
|
32
|
+
PartyResource::Connector.default = name
|
33
|
+
PartyResource::Connector.repository.default.should == name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#new' do
|
38
|
+
let_mock(:name)
|
39
|
+
let_mock(:options)
|
40
|
+
let_mock(:connector)
|
41
|
+
|
42
|
+
before do
|
43
|
+
@repository = PartyResource::Connector::Repository.new
|
44
|
+
PartyResource::Connector.stub(:repository => @repository)
|
45
|
+
PartyResource::Connector::Base.stub(:new => connector)
|
46
|
+
options.stub(:[]).with(:default).and_return(false)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'creates a new connector' do
|
50
|
+
PartyResource::Connector::Base.should_receive(:new).with(name, options).and_return(connector)
|
51
|
+
PartyResource::Connector.add(name, options)
|
52
|
+
PartyResource::Connector(name).should == connector
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'sets the default if it is currently unset' do
|
56
|
+
name2 = mock(:name2)
|
57
|
+
PartyResource::Connector.add(name, options)
|
58
|
+
PartyResource::Connector.repository.default.should == name
|
59
|
+
|
60
|
+
PartyResource::Connector.add(name2, options)
|
61
|
+
PartyResource::Connector.repository.default.should == name
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'sets the default if the default option is set' do
|
65
|
+
name2 = mock(:name2)
|
66
|
+
PartyResource::Connector.add(name, options)
|
67
|
+
PartyResource::Connector.repository.default.should == name
|
68
|
+
|
69
|
+
options.stub(:[]).with(:default).and_return(true)
|
70
|
+
PartyResource::Connector.add(name2, options)
|
71
|
+
PartyResource::Connector.repository.default.should == name2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Exceptions' do
|
4
|
+
|
5
|
+
describe PartyResource::ConnectionError do
|
6
|
+
subject { PartyResource::ConnectionError }
|
7
|
+
|
8
|
+
describe 'build' do
|
9
|
+
let(:built) { subject.build(data) }
|
10
|
+
let_mock(:data)
|
11
|
+
|
12
|
+
it 'builds ResourceNotFound' do
|
13
|
+
data.stub(:code => 404)
|
14
|
+
built.should be_a(PartyResource::ResourceNotFound)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'builds ResourceInvalid' do
|
18
|
+
data.stub(:code => 422)
|
19
|
+
built.should be_a(PartyResource::ResourceInvalid)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'builds ClientError' do
|
23
|
+
data.stub(:code => 400)
|
24
|
+
built.should be_a(PartyResource::ClientError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'builds ServerError' do
|
28
|
+
data.stub(:code => 500)
|
29
|
+
built.should be_a(PartyResource::ServerError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require File.expand_path(File.join(__FILE__, '..', '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe "PartyResource" do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
Class.new().send(:include, PartyResource)
|
7
|
+
end
|
8
|
+
let(:object) { subject.new }
|
9
|
+
|
10
|
+
describe '.connect' do
|
11
|
+
|
12
|
+
let(:route) { mock(:route) }
|
13
|
+
let_mock(:other_options)
|
14
|
+
|
15
|
+
before do
|
16
|
+
PartyResource::Route.stub(:new => route)
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'for a class level route' do
|
20
|
+
let_mock(:klass)
|
21
|
+
|
22
|
+
it "creates a class method matching the name" do
|
23
|
+
subject.connect :new_resource_method
|
24
|
+
should respond_to(:new_resource_method)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "creates a new route" do
|
28
|
+
options = {:values => other_options, :as => klass}
|
29
|
+
PartyResource::Route.should_receive(:new).with({:values => other_options, :connector => nil, :as => klass})
|
30
|
+
subject.connect :new_resource_method, options
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'the created method' do
|
34
|
+
|
35
|
+
before { subject.connect :new_resource_method }
|
36
|
+
|
37
|
+
it 'calls the route with the class and the arguments' do
|
38
|
+
args = [mock(:arg), mock(:arg)]
|
39
|
+
route.should_receive(:call).with(subject, *args)
|
40
|
+
|
41
|
+
subject.new_resource_method args[0], args[1]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with a connector set' do
|
47
|
+
before do
|
48
|
+
subject.party_connector :foo
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'passes the connector name to the route' do
|
52
|
+
PartyResource::Route.should_receive(:new).with(hash_including(:connector => :foo))
|
53
|
+
subject.connect :new_resource_method, {}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'for an instance level route' do
|
59
|
+
|
60
|
+
it "creates an instance method matching the name" do
|
61
|
+
subject.connect :new_resource_method, :on => :instance
|
62
|
+
subject.new.should respond_to(:new_resource_method)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "creates a new route" do
|
66
|
+
options = {:on => :instance, :others => other_options}
|
67
|
+
PartyResource::Route.should_receive(:new).with({:others => other_options, :connector => nil, :as => :self})
|
68
|
+
subject.connect :new_resource_method, options
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'the created method' do
|
72
|
+
|
73
|
+
before { subject.connect :new_resource_method, :on => :instance }
|
74
|
+
|
75
|
+
it 'calls the route with the class and the arguments' do
|
76
|
+
args = [mock(:arg), mock(:arg)]
|
77
|
+
object = subject.new
|
78
|
+
route.should_receive(:call).with(object, *args)
|
79
|
+
|
80
|
+
object.new_resource_method args[0], args[1]
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '.Connector' do
|
88
|
+
context 'with name == nil' do
|
89
|
+
it 'returns the default connector' do
|
90
|
+
connector = mock(:connector)
|
91
|
+
PartyResource::Connector.should_receive(:lookup).with(nil).and_return(connector)
|
92
|
+
PartyResource::Connector(nil).should == connector
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'with a name given' do
|
97
|
+
it 'returns the requested connector' do
|
98
|
+
connector = mock(:connector)
|
99
|
+
name = mock(:name)
|
100
|
+
PartyResource::Connector.should_receive(:lookup).with(name).and_return(connector)
|
101
|
+
PartyResource::Connector(name).should == connector
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '.parameter_values' do
|
107
|
+
let_mock(:v1)
|
108
|
+
let_mock(:v2)
|
109
|
+
let_mock(:v3)
|
110
|
+
it 'returns internal values for requested variables' do
|
111
|
+
subject.stub(:v1 => v1, :v2 => v2, :v3 => v3)
|
112
|
+
subject.parameter_values([:v1, :v3]).should == {:v1 => v1, :v3 => v3}
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'raises a MissingParameter error' do
|
116
|
+
subject.stub(:v1 => v1)
|
117
|
+
lambda { subject.parameter_values([:v1, :vx]) }.should raise_error(PartyResource::MissingParameter)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'properties' do
|
122
|
+
let_mock(:v1)
|
123
|
+
let_mock(:v2)
|
124
|
+
it 'populates property values from hash' do
|
125
|
+
subject.property :name, :name2
|
126
|
+
object.send(:populate_properties, :name => v1, :name2 => v2)
|
127
|
+
object.name.should == v1
|
128
|
+
object.name2.should == v2
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'renames properties from passed data' do
|
132
|
+
subject.property :name, :from => :input_name
|
133
|
+
object.send(:populate_properties, :input_name => v1, :name => v2)
|
134
|
+
object.name.should == v1
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'reaches inside nested values' do
|
138
|
+
subject.property :name, :from => [:parent, :input_name]
|
139
|
+
object.send(:populate_properties, :parent => { :input_name => v1 }, :input_name => v2)
|
140
|
+
object.name.should == v1
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'falls back to populating based on the property name if from is not found' do
|
144
|
+
subject.property :name, :from => :input_name
|
145
|
+
object.send(:populate_properties, :name => v2)
|
146
|
+
object.name.should == v2
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'does not try to build nil values' do
|
150
|
+
klass = Class.new
|
151
|
+
subject.property :name, :as => klass
|
152
|
+
klass.should_not_receive(:new)
|
153
|
+
object.send(:populate_properties, :name => nil)
|
154
|
+
object.name.should == nil
|
155
|
+
end
|
156
|
+
|
157
|
+
it "doesn't set properties to nil if they aren't in the data hash" do
|
158
|
+
subject.property :name, :name2
|
159
|
+
object.send(:populate_properties, :name => v1)
|
160
|
+
object.name.should == v1
|
161
|
+
object.name2.should == nil
|
162
|
+
object.send(:populate_properties,:name2 => v2)
|
163
|
+
object.name.should == v1
|
164
|
+
object.name2.should == v2
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'returns a hash representation of properties' do
|
168
|
+
subject.property :name, :name2
|
169
|
+
object.send(:populate_properties, :name => v1, :name2 => v2)
|
170
|
+
object.to_properties_hash.should == {:name => v1, :name2 => v2}
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'does not include nil values in the properties hash' do
|
174
|
+
subject.property :name, :name2
|
175
|
+
object.send(:populate_properties, :name => v1)
|
176
|
+
object.to_properties_hash.should == {:name => v1}
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'translates propery names to input names' do
|
180
|
+
subject.property :name, :from => :input_name
|
181
|
+
object.send(:populate_properties, :name => v1)
|
182
|
+
object.to_properties_hash.should == {:input_name => v1}
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'translates propery names to nested input names' do
|
186
|
+
subject.property :name, :from => [:block, :input_name]
|
187
|
+
object.send(:populate_properties, :name => v1)
|
188
|
+
object.to_properties_hash.should == {:block => {:input_name => v1}}
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'translates property names to their required output names' do
|
192
|
+
subject.property :name, :to => :output_name
|
193
|
+
subject.property :name2, :to => [:block, :output_name]
|
194
|
+
object.send(:populate_properties, :name => v1, :name2 => v2)
|
195
|
+
object.to_properties_hash.should == {:output_name => v1, :block => {:output_name => v2}}
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe '#properties_equal?' do
|
200
|
+
let_mock(:v1)
|
201
|
+
let(:object2) { subject.new }
|
202
|
+
|
203
|
+
before do
|
204
|
+
subject.property :name, :name2
|
205
|
+
object.send(:populate_properties, :name => v1, :name2 => nil)
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'returns true if all properties are equal' do
|
209
|
+
subject.property :name, :name2
|
210
|
+
object.send(:populate_properties, :name => v1, :name2 => nil)
|
211
|
+
object2.send(:populate_properties, :name => v1)
|
212
|
+
object.should be_properties_equal(object2)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'returns false if all properties are not equal' do
|
216
|
+
object2.send(:populate_properties, :name => v1, :name2 => v1)
|
217
|
+
object.should_not be_properties_equal(object2)
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'returns false if the other object does not respond to all required properties' do
|
221
|
+
object.should_not be_properties_equal(Object.new)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
end
|
227
|
+
|