frenetic 0.0.12 → 0.0.20.alpha.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.
- data/.gitignore +1 -0
- data/.irbrc +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +0 -1
- data/Gemfile +1 -3
- data/README.md +138 -125
- data/frenetic.gemspec +5 -6
- data/lib/frenetic.rb +31 -43
- data/lib/frenetic/concerns/collection_rest_methods.rb +13 -0
- data/lib/frenetic/concerns/configurable.rb +59 -0
- data/lib/frenetic/concerns/hal_linked.rb +59 -0
- data/lib/frenetic/concerns/member_rest_methods.rb +15 -0
- data/lib/frenetic/concerns/structured.rb +53 -0
- data/lib/frenetic/configuration.rb +40 -76
- data/lib/frenetic/middleware/hal_json.rb +23 -0
- data/lib/frenetic/resource.rb +77 -62
- data/lib/frenetic/resource_collection.rb +46 -0
- data/lib/frenetic/version.rb +2 -2
- data/spec/concerns/configurable_spec.rb +50 -0
- data/spec/concerns/hal_linked_spec.rb +116 -0
- data/spec/concerns/member_rest_methods_spec.rb +41 -0
- data/spec/concerns/structured_spec.rb +214 -0
- data/spec/configuration_spec.rb +99 -0
- data/spec/fixtures/test_api_requests.rb +88 -0
- data/spec/frenetic_spec.rb +137 -0
- data/spec/middleware/hal_json_spec.rb +83 -0
- data/spec/resource_collection_spec.rb +80 -0
- data/spec/resource_spec.rb +211 -0
- data/spec/spec_helper.rb +4 -13
- data/spec/support/rspec.rb +5 -0
- data/spec/support/webmock.rb +3 -0
- metadata +59 -57
- data/.rvmrc +0 -1
- data/lib/frenetic/hal_json.rb +0 -23
- data/lib/frenetic/hal_json/response_wrapper.rb +0 -43
- data/lib/recursive_open_struct.rb +0 -79
- data/spec/fixtures/vcr_cassettes/description_error_unauthorized.yml +0 -36
- data/spec/fixtures/vcr_cassettes/description_success.yml +0 -38
- data/spec/lib/frenetic/configuration_spec.rb +0 -189
- data/spec/lib/frenetic/hal_json/response_wrapper_spec.rb +0 -70
- data/spec/lib/frenetic/hal_json_spec.rb +0 -68
- data/spec/lib/frenetic/resource_spec.rb +0 -182
- data/spec/lib/frenetic_spec.rb +0 -129
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'frenetic/concerns/hal_linked'
|
2
|
+
require 'frenetic/concerns/collection_rest_methods'
|
3
|
+
|
4
|
+
class Frenetic
|
5
|
+
class ResourceCollection < Delegator
|
6
|
+
include HalLinked
|
7
|
+
include CollectionRestMethods
|
8
|
+
|
9
|
+
def initialize( resource, params = {} )
|
10
|
+
@resource_class = resource
|
11
|
+
@resources = []
|
12
|
+
@params = params
|
13
|
+
|
14
|
+
extract_resources!
|
15
|
+
end
|
16
|
+
|
17
|
+
def resource_type
|
18
|
+
@resource_type ||= @resource_class.to_s.demodulize.underscore
|
19
|
+
end
|
20
|
+
|
21
|
+
def collection_key
|
22
|
+
@collection_key ||= resource_type.pluralize
|
23
|
+
end
|
24
|
+
|
25
|
+
def __getobj__
|
26
|
+
@resources
|
27
|
+
end
|
28
|
+
|
29
|
+
def __setobj__
|
30
|
+
@resources
|
31
|
+
end
|
32
|
+
|
33
|
+
def api
|
34
|
+
@resources.first.api
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def extract_resources!
|
40
|
+
@resources = @params['_embedded'][collection_key].collect do |resource|
|
41
|
+
@resource_class.new resource
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
data/lib/frenetic/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
class Frenetic
|
2
|
-
VERSION = '0.0.
|
3
|
-
end
|
2
|
+
VERSION = '0.0.20.alpha.0'
|
3
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
describe Frenetic::Configurable do
|
2
|
+
let(:test_cfg) do
|
3
|
+
{
|
4
|
+
url:'http://example.org'
|
5
|
+
}
|
6
|
+
end
|
7
|
+
|
8
|
+
subject(:instance) { Frenetic.new( test_cfg ) }
|
9
|
+
|
10
|
+
describe '#config' do
|
11
|
+
subject { instance.config }
|
12
|
+
|
13
|
+
it { should_not be_empty }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#configure' do
|
17
|
+
subject do
|
18
|
+
cfg = nil
|
19
|
+
instance.configure { |c| cfg = c }
|
20
|
+
cfg
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should yield the configuration' do
|
24
|
+
subject.should be_a Hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '.configure' do
|
29
|
+
subject { Frenetic.configure{|c|} }
|
30
|
+
|
31
|
+
it 'should not exist' do
|
32
|
+
expect{ subject }.to raise_error NoMethodError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#initialize' do
|
37
|
+
let(:callback) do
|
38
|
+
Proc.new { |b| }
|
39
|
+
end
|
40
|
+
|
41
|
+
subject do
|
42
|
+
Frenetic.new( &callback ).instance_variable_get( "@builder_config" )
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'retain block arguments' do
|
46
|
+
subject.should eq callback
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Frenetic::HalLinked do
|
4
|
+
let(:test_cfg) { { url:'http://example.com/api' } }
|
5
|
+
|
6
|
+
let(:my_temp_resource) do
|
7
|
+
cfg = test_cfg
|
8
|
+
|
9
|
+
Class.new(Frenetic::Resource) do
|
10
|
+
api_client { Frenetic.new(cfg) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub_const 'MyTempResource', my_temp_resource
|
16
|
+
|
17
|
+
MyTempResource.send :include, described_class
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#links' do
|
21
|
+
before { @stubs.api_description }
|
22
|
+
|
23
|
+
let(:_links) do
|
24
|
+
{
|
25
|
+
'_links' => { 'self' => { 'href' => '/api/self' }}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
subject { MyTempResource.new(_links).links }
|
30
|
+
|
31
|
+
it 'should return the instances links' do
|
32
|
+
subject.should include 'self'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#member_url' do
|
37
|
+
before { @stubs.api_description }
|
38
|
+
|
39
|
+
subject { MyTempResource.new(_links).member_url }
|
40
|
+
|
41
|
+
let(:_links) do
|
42
|
+
{
|
43
|
+
'_links' => {
|
44
|
+
'self' => { 'href' => '/api/self' },
|
45
|
+
'my_temp_resource' => {
|
46
|
+
'href' => '/api/my_temp_resource/{id}', 'templated' => true
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with a link that matches the resource name' do
|
53
|
+
it 'should return the named link' do
|
54
|
+
subject.should == '/api/my_temp_resource/'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with an implied self link' do
|
59
|
+
let(:_links) do
|
60
|
+
{
|
61
|
+
'_links' => { 'self' => { 'href' => '/api/self' }}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should return the :self link' do
|
66
|
+
subject.should == '/api/self'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '.member_url' do
|
72
|
+
let(:args) {}
|
73
|
+
|
74
|
+
subject { MyTempResource.member_url args }
|
75
|
+
|
76
|
+
before { @stubs.api_description }
|
77
|
+
|
78
|
+
context 'for an unknown resource' do
|
79
|
+
before do
|
80
|
+
MyTempResource.stub(:namespace).and_return Time.now.to_i.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should raise an error' do
|
84
|
+
expect{ subject }.to raise_error Frenetic::HypermediaError
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with a non-templated URL' do
|
89
|
+
before do
|
90
|
+
MyTempResource.stub(:namespace).and_return 'abstract_resource'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'simply return the URL' do
|
94
|
+
subject.should == '/api/abstract_resource'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'with a templated URL' do
|
99
|
+
context 'and a non-Hash argument' do
|
100
|
+
let(:args) { 1 }
|
101
|
+
|
102
|
+
it 'should expand the URL template' do
|
103
|
+
subject.should == '/api/my_temp_resources/1'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'with a Hash argument' do
|
108
|
+
let(:args) { { id:1 } }
|
109
|
+
|
110
|
+
it 'should interpolate the URL' do
|
111
|
+
subject.should == '/api/my_temp_resources/1'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Frenetic::MemberRestMethods do
|
4
|
+
let(:test_cfg) { { url:'http://example.com/api' } }
|
5
|
+
|
6
|
+
let(:my_temp_resource) do
|
7
|
+
cfg = test_cfg
|
8
|
+
|
9
|
+
Class.new(Frenetic::Resource) do
|
10
|
+
api_client { Frenetic.new(cfg) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub_const 'MyTempResource', my_temp_resource
|
16
|
+
|
17
|
+
MyTempResource.send :include, described_class
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.find' do
|
21
|
+
before { @stubs.api_description }
|
22
|
+
|
23
|
+
subject { MyTempResource.find 1 }
|
24
|
+
|
25
|
+
context 'for a known instance' do
|
26
|
+
before { @stubs.known_resource }
|
27
|
+
|
28
|
+
it 'should return the instance' do
|
29
|
+
expect(subject).to be_a MyTempResource
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'for an unknown instance' do
|
34
|
+
before { @stubs.unknown_resource }
|
35
|
+
|
36
|
+
it 'should raise an error' do
|
37
|
+
expect{ subject }.to raise_error Frenetic::ClientError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Frenetic::Structured do
|
4
|
+
let(:my_temp_resource) do
|
5
|
+
Class.new do
|
6
|
+
def initialize( attrs = {} )
|
7
|
+
@attrs = attrs
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
stub_const 'MyTempResource', my_temp_resource
|
14
|
+
|
15
|
+
MyTempResource.send :include, described_class
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
instance.rspec_reset
|
20
|
+
|
21
|
+
instance.destroy_structure!
|
22
|
+
end
|
23
|
+
|
24
|
+
subject(:instance) { MyTempResource.new( foo:'foo', bar:'bar' ) }
|
25
|
+
|
26
|
+
describe '#struct_key' do
|
27
|
+
subject { instance.struct_key }
|
28
|
+
|
29
|
+
it 'should return a valid, unique Ruby constant name' do
|
30
|
+
subject.should == 'MyTempResourceFreneticResourceStruct'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#signature' do
|
35
|
+
subject { instance.signature }
|
36
|
+
|
37
|
+
it 'should return a unique and predictable key' do
|
38
|
+
subject.should == 'barfoo'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#structure' do
|
43
|
+
subject { instance.structure }
|
44
|
+
|
45
|
+
context 'with no previously defined signature' do
|
46
|
+
before do
|
47
|
+
instance.stub( :structure_expired? ).and_return true
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should rebuild the structure' do
|
51
|
+
instance.should_receive :rebuild_structure!
|
52
|
+
|
53
|
+
subject
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'with a fresh signature' do
|
58
|
+
before do
|
59
|
+
instance.structure
|
60
|
+
|
61
|
+
instance.stub( :structure_expired? ).and_return false
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should not rebuild the structure' do
|
65
|
+
instance.should_receive( :rebuild_structure! ).never
|
66
|
+
|
67
|
+
subject
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with an expired signature' do
|
72
|
+
before do
|
73
|
+
instance.structure
|
74
|
+
|
75
|
+
instance.stub( :structure_expired? ).and_return true
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should rebuild the structure' do
|
79
|
+
instance.should_receive :rebuild_structure!
|
80
|
+
|
81
|
+
subject
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#fetch_structure' do
|
87
|
+
before { instance.structure }
|
88
|
+
|
89
|
+
subject { instance.fetch_structure }
|
90
|
+
|
91
|
+
it "should return the resource's Struct class" do
|
92
|
+
subject.should == Struct::MyTempResourceFreneticResourceStruct
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#rebuild_structure!' do
|
97
|
+
before{ instance.structure }
|
98
|
+
|
99
|
+
subject { instance.rebuild_structure! }
|
100
|
+
|
101
|
+
it 'should destroy the previous Struct' do
|
102
|
+
instance.should_receive(:destroy_structure!).and_call_original
|
103
|
+
|
104
|
+
subject
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should add cache the resource's signature" do
|
108
|
+
sigs = described_class.class_variable_get('@@signatures')
|
109
|
+
|
110
|
+
sigs.should include
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should build the resource's Struct" do
|
114
|
+
subject
|
115
|
+
|
116
|
+
instance.fetch_structure.members.should == [:foo, :bar]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#structure_expired?' do
|
121
|
+
subject { instance.structure_expired? }
|
122
|
+
|
123
|
+
before do
|
124
|
+
instance.stub( :signature ).and_return new_sig
|
125
|
+
described_class.class_variable_set '@@signatures', {
|
126
|
+
'MyTempResourceFreneticResourceStruct' => old_sig
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with a fresh signature' do
|
131
|
+
let(:old_sig) { 'fresh' }
|
132
|
+
let(:new_sig) { 'fresh' }
|
133
|
+
|
134
|
+
it { should be_false }
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'with no predefined signature' do
|
138
|
+
let(:old_sig) { nil }
|
139
|
+
let(:new_sig) { 'new' }
|
140
|
+
|
141
|
+
it { should be_true }
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'with an expired signature' do
|
145
|
+
let(:old_sig) { 'old' }
|
146
|
+
let(:new_sig) { 'new' }
|
147
|
+
|
148
|
+
it { should be_true }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#structure_defined?' do
|
153
|
+
subject { instance.structure_defined? }
|
154
|
+
|
155
|
+
let(:consts) { Struct.constants || [] }
|
156
|
+
|
157
|
+
before do
|
158
|
+
Struct.stub(:constants).and_return consts
|
159
|
+
end
|
160
|
+
|
161
|
+
after { Struct.rspec_reset }
|
162
|
+
|
163
|
+
it 'should check if the structure is defined' do
|
164
|
+
instance.stub(:struct_key).and_return 'Foo'
|
165
|
+
|
166
|
+
consts.should_receive( :include? ).with :Foo
|
167
|
+
|
168
|
+
subject
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#destroy_structure!' do
|
173
|
+
before { instance.structure }
|
174
|
+
|
175
|
+
subject { instance.destroy_structure! }
|
176
|
+
|
177
|
+
context 'with an undefined structure' do
|
178
|
+
before { instance.destroy_structure! }
|
179
|
+
|
180
|
+
it "should not attempt to remove the structure's constant" do
|
181
|
+
Struct.should_receive( :remove_const ).never
|
182
|
+
|
183
|
+
subject
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should not remove the signature from the cache' do
|
187
|
+
described_class.class_variable_get('@@signatures').should_receive( :delete ).never
|
188
|
+
|
189
|
+
subject
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'with an predefined structure' do
|
194
|
+
it 'should remove the constant' do
|
195
|
+
Struct.constants.should include instance.struct_key.to_sym
|
196
|
+
|
197
|
+
subject
|
198
|
+
|
199
|
+
Struct.constants.should_not include instance.struct_key.to_sym
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'should remove the signature from the cache' do
|
203
|
+
signature = { 'MyTempResourceFreneticResourceStruct' => 'barfoo' }
|
204
|
+
|
205
|
+
described_class.class_variable_get('@@signatures').should include signature
|
206
|
+
|
207
|
+
subject
|
208
|
+
|
209
|
+
described_class.class_variable_get('@@signatures').should_not include signature
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|