frenetic 0.0.20.alpha.6 → 1.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -1
  3. data/Appraisals +9 -0
  4. data/Gemfile +1 -1
  5. data/README.md +2 -2
  6. data/frenetic.gemspec +8 -5
  7. data/gemfiles/faraday_08.gemfile +10 -0
  8. data/gemfiles/faraday_08.gemfile.lock +77 -0
  9. data/gemfiles/faraday_09.gemfile +10 -0
  10. data/gemfiles/faraday_09.gemfile.lock +77 -0
  11. data/lib/frenetic.rb +57 -30
  12. data/lib/frenetic/briefly_memoizable.rb +34 -0
  13. data/lib/frenetic/concerns/collection_rest_methods.rb +1 -1
  14. data/lib/frenetic/concerns/hal_linked.rb +5 -35
  15. data/lib/frenetic/concerns/member_rest_methods.rb +0 -2
  16. data/lib/frenetic/concerns/structured.rb +0 -5
  17. data/lib/frenetic/connection.rb +110 -0
  18. data/lib/frenetic/errors.rb +112 -0
  19. data/lib/frenetic/hypermedia_link.rb +74 -0
  20. data/lib/frenetic/hypermedia_link_set.rb +43 -0
  21. data/lib/frenetic/middleware/hal_json.rb +9 -12
  22. data/lib/frenetic/resource.rb +22 -6
  23. data/lib/frenetic/resource_collection.rb +0 -1
  24. data/lib/frenetic/resource_mockery.rb +55 -1
  25. data/lib/frenetic/version.rb +1 -1
  26. data/spec/{concerns/breifly_memoizable_spec.rb → briefly_memoizable_spec.rb} +10 -18
  27. data/spec/concerns/hal_linked_spec.rb +49 -62
  28. data/spec/concerns/member_rest_methods_spec.rb +8 -10
  29. data/spec/concerns/structured_spec.rb +70 -75
  30. data/spec/connection_spec.rb +137 -0
  31. data/spec/fixtures/test_api_requests.rb +8 -2
  32. data/spec/frenetic_spec.rb +221 -133
  33. data/spec/hypermedia_link_set_spec.rb +155 -0
  34. data/spec/hypermedia_link_spec.rb +153 -0
  35. data/spec/middleware/hal_json_spec.rb +13 -15
  36. data/spec/resource_collection_spec.rb +17 -16
  37. data/spec/resource_mockery_spec.rb +69 -0
  38. data/spec/resource_spec.rb +110 -63
  39. data/spec/support/rspec.rb +0 -1
  40. metadata +88 -75
  41. data/lib/frenetic/concerns/briefly_memoizable.rb +0 -34
  42. data/lib/frenetic/concerns/configurable.rb +0 -59
  43. data/lib/frenetic/concerns/resource_mockery.rb +0 -48
  44. data/lib/frenetic/configuration.rb +0 -88
  45. data/spec/concerns/configurable_spec.rb +0 -50
  46. data/spec/concerns/resource_mockery_spec.rb +0 -56
  47. data/spec/configuration_spec.rb +0 -134
@@ -45,6 +45,5 @@ class Frenetic
45
45
  def embedded_collection
46
46
  @params.fetch('_embedded',{}).fetch(collection_key, [])
47
47
  end
48
-
49
48
  end
50
49
  end
@@ -1 +1,55 @@
1
- require 'frenetic/concerns/resource_mockery'
1
+ require 'ostruct'
2
+ require 'delegate'
3
+ require 'active_support/concern'
4
+
5
+ class Frenetic
6
+ module ResourceMockery
7
+ extend Forwardable
8
+ extend ActiveSupport::Concern
9
+
10
+ def_delegators :@params, :to_json
11
+
12
+ included do
13
+ # I'm sure this violates some sort of CS principle or best practice,
14
+ # but it solves the problem for now.
15
+ superclass.send :instance_variable_set, '@mock_class', self
16
+ end
17
+
18
+ def attributes
19
+ @params
20
+ end
21
+
22
+ def properties
23
+ @params.each_with_object({}) do |(k,v), props|
24
+ props[k] = v.class.to_s.underscore
25
+ end
26
+ end
27
+
28
+ def default_attributes
29
+ self.class.default_attributes
30
+ end
31
+
32
+ module ClassMethods
33
+ def api_client
34
+ superclass.api_client
35
+ end
36
+
37
+ # Provides a place for a Resources that are mocked to declare reasonable
38
+ # default values for Mock Resources
39
+ def default_attributes
40
+ {}
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def build_params( p )
47
+ defaults = default_attributes.with_indifferent_access
48
+ @params = defaults.merge( (p || {}).with_indifferent_access )
49
+ end
50
+
51
+ def build_structure
52
+ @structure = OpenStruct.new( @attrs )
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,3 @@
1
1
  class Frenetic
2
- VERSION = '0.0.20.alpha.6'
2
+ VERSION = '1.0.0.alpha.1'
3
3
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe BrieflyMemoizable do
3
+ describe Frenetic::BrieflyMemoizable do
4
4
  let(:my_class) { Class.new }
5
5
 
6
6
  before do
@@ -10,7 +10,6 @@ describe BrieflyMemoizable do
10
10
  MyClass.class_eval do
11
11
  def fetch
12
12
  @fetch_age = Time.now + 3600
13
-
14
13
  external_call
15
14
  end
16
15
  briefly_memoize :fetch
@@ -27,40 +26,33 @@ describe BrieflyMemoizable do
27
26
  context 'for an expensive method' do
28
27
  before do
29
28
  Timecop.freeze
30
-
31
29
  instance.fetch
30
+ allow(instance).to receive(:external_call).and_call_original
32
31
  end
33
32
 
34
33
  context 'which is called again outside the memoize window' do
35
- before do
36
- Timecop.travel Time.now + 5400
37
- end
38
-
39
- it 'should be called' do
40
- instance.should_receive(:external_call).once.and_call_original
34
+ before { Timecop.travel Time.now + 5400 }
41
35
 
36
+ it 'is called' do
42
37
  instance.fetch
38
+ expect(instance).to have_received(:external_call).once
43
39
  end
44
40
  end
45
41
 
46
42
  context 'which is called again within the memoize window' do
47
- before do
48
- Timecop.travel Time.now + 1800
49
- end
50
-
51
- it 'should not be called' do
52
- instance.should_receive(:external_call).never.and_call_original
43
+ before { Timecop.travel Time.now + 1800 }
53
44
 
45
+ it 'is not called' do
54
46
  instance.fetch
47
+ expect(instance).to_not have_received(:external_call)
55
48
  end
56
49
 
57
50
  context 'after it has been reloaded' do
58
51
  before { instance.reload_fetch! }
59
52
 
60
- it 'should be called' do
61
- instance.should_receive(:external_call).once.and_call_original
62
-
53
+ it 'is called' do
63
54
  instance.fetch
55
+ expect(instance).to have_received(:external_call).once
64
56
  end
65
57
  end
66
58
  end
@@ -13,7 +13,6 @@ describe Frenetic::HalLinked do
13
13
 
14
14
  before do
15
15
  stub_const 'MyTempResource', my_temp_resource
16
-
17
16
  MyTempResource.send :include, described_class
18
17
  end
19
18
 
@@ -28,8 +27,8 @@ describe Frenetic::HalLinked do
28
27
 
29
28
  subject { MyTempResource.new(_links).links }
30
29
 
31
- it 'should return the instances links' do
32
- subject.should include 'self'
30
+ it 'returns the instances links' do
31
+ expect(subject).to include 'self'
33
32
  end
34
33
  end
35
34
 
@@ -38,20 +37,28 @@ describe Frenetic::HalLinked do
38
37
 
39
38
  subject { MyTempResource.new(_links).member_url }
40
39
 
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
40
+ context 'with a link that matches the resource name' do
41
+ let(:_links) do
42
+ {
43
+ '_links' => {
44
+ 'self' => { 'href' => '/api/self' },
45
+ 'my_temp_resource' => {
46
+ 'href' => '/api/my_temp_resource'
47
+ }
47
48
  }
48
49
  }
49
- }
50
- end
50
+ end
51
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/'
52
+ it 'processes the link' do
53
+ expect_any_instance_of(Frenetic::HypermediaLinkSet)
54
+ .to receive(:href).with({}).and_call_original
55
+ subject
56
+ end
57
+
58
+ it 'finds the appropriate link' do
59
+ # Admittedly, this isn't the best test in the world, but I wanted to
60
+ # ensure that the correct link is found out of the set
61
+ expect(subject).to eq '/api/my_temp_resource'
55
62
  end
56
63
  end
57
64
 
@@ -62,76 +69,56 @@ describe Frenetic::HalLinked do
62
69
  }
63
70
  end
64
71
 
65
- it 'should return the :self link' do
66
- subject.should == '/api/self'
72
+ it 'processes the link' do
73
+ expect_any_instance_of(Frenetic::HypermediaLinkSet)
74
+ .to receive(:href).with({}).and_call_original
75
+ subject
76
+ end
77
+
78
+ it 'returns the :self link' do
79
+ # Admittedly, this isn't the best test in the world, but I wanted to
80
+ # ensure that the correct link is found out of the set
81
+ expect(subject).to eq '/api/self'
67
82
  end
68
83
  end
69
84
  end
70
85
 
71
86
  describe '.member_url' do
72
- let(:args) {}
73
-
74
- subject { MyTempResource.member_url args }
87
+ let(:params) { { id:1 } }
75
88
 
76
89
  before { @stubs.api_description }
77
90
 
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
91
+ subject { MyTempResource.member_url params }
106
92
 
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
93
+ it 'processes the link' do
94
+ expect_any_instance_of(Frenetic::HypermediaLinkSet)
95
+ .to receive(:href).with( params ).and_call_original
96
+ subject
114
97
  end
115
98
  end
116
99
 
117
100
  describe '.collection_url' do
118
- subject { MyTempResource.collection_url }
119
-
120
101
  before { @stubs.api_description }
121
102
 
103
+ subject { MyTempResource.collection_url }
104
+
122
105
  context 'for an unknown resource' do
123
106
  before do
124
- MyTempResource.stub(:namespace).and_return Time.now.to_i.to_s
107
+ allow(MyTempResource)
108
+ .to receive(:namespace)
109
+ .and_return(Time.now.to_i.to_s)
125
110
  end
126
111
 
127
- it 'should raise an error' do
128
- expect{ subject }.to raise_error Frenetic::HypermediaError
112
+ it 'raises an error' do
113
+ expect{subject}.to raise_error Frenetic::HypermediaError
129
114
  end
130
115
  end
131
116
 
132
- context 'with a non-templated URL' do
133
- it 'simply return the URL' do
134
- subject.should == '/api/my_temp_resources'
117
+ context 'for a known resource' do
118
+ it 'processes the link' do
119
+ expect_any_instance_of(Frenetic::HypermediaLinkSet)
120
+ .to receive(:href).and_call_original
121
+ subject
135
122
  end
136
123
  end
137
124
  end
@@ -13,7 +13,6 @@ describe Frenetic::MemberRestMethods do
13
13
 
14
14
  before do
15
15
  stub_const 'MyTempResource', my_temp_resource
16
-
17
16
  MyTempResource.send :include, described_class
18
17
  end
19
18
 
@@ -25,14 +24,14 @@ describe Frenetic::MemberRestMethods do
25
24
  context 'for a known instance' do
26
25
  before { @stubs.known_instance }
27
26
 
28
- it 'should return the instance' do
27
+ it 'returns the instance' do
29
28
  expect(subject).to be_a MyTempResource
30
29
  end
31
30
 
32
31
  context 'and a Hash argument' do
33
32
  subject { MyTempResource.find id:1 }
34
33
 
35
- it 'should return the instance' do
34
+ it 'returns the instance' do
36
35
  expect(subject).to be_a MyTempResource
37
36
  end
38
37
  end
@@ -41,8 +40,8 @@ describe Frenetic::MemberRestMethods do
41
40
  context 'for an unknown instance' do
42
41
  before { @stubs.unknown_instance }
43
42
 
44
- it 'should raise an error' do
45
- expect{ subject }.to raise_error Frenetic::ClientError
43
+ it 'raises an error' do
44
+ expect{subject}.to raise_error Frenetic::ClientError
46
45
  end
47
46
  end
48
47
 
@@ -51,11 +50,10 @@ describe Frenetic::MemberRestMethods do
51
50
 
52
51
  before do
53
52
  stub_const 'MyMockResource', Class.new(MyTempResource)
54
-
55
53
  MyMockResource.send :include, Frenetic::ResourceMockery
56
54
  end
57
55
 
58
- it 'should return a mock resource' do
56
+ it 'returns a mock resource' do
59
57
  expect(subject).to be_an_instance_of MyMockResource
60
58
  end
61
59
  end
@@ -69,11 +67,11 @@ describe Frenetic::MemberRestMethods do
69
67
  context 'for a known resource' do
70
68
  before { @stubs.known_resource }
71
69
 
72
- it 'should return a resource collection' do
70
+ it 'returns a resource collection' do
73
71
  expect(subject).to be_an_instance_of Frenetic::ResourceCollection
74
72
  end
75
73
 
76
- it 'should instantiate all resources in the collection' do
74
+ it 'instantiates all resources in the collection' do
77
75
  expect(subject.first).to be_an_instance_of MyTempResource
78
76
  end
79
77
  end
@@ -81,7 +79,7 @@ describe Frenetic::MemberRestMethods do
81
79
  context 'in test mode' do
82
80
  let(:test_cfg) { { url:'http://example.com/api', test_mode:true } }
83
81
 
84
- it 'should return an empty collection' do
82
+ it 'returns an empty collection' do
85
83
  expect(subject).to be_empty
86
84
  end
87
85
  end
@@ -9,76 +9,77 @@ describe Frenetic::Structured do
9
9
  end
10
10
  end
11
11
 
12
+ let(:signatures) do
13
+ described_class.class_variable_get('@@signatures')
14
+ end
15
+
16
+ let(:signature) do
17
+ { 'MyTempResourceFreneticResourceStruct' => 'barfoo' }
18
+ end
19
+
12
20
  before do
13
21
  stub_const 'MyTempResource', my_temp_resource
14
-
15
22
  MyTempResource.send :include, described_class
16
23
  end
17
24
 
18
- after do
19
- instance.rspec_reset
20
-
21
- instance.destroy_structure!
22
- end
25
+ after { instance.destroy_structure! }
23
26
 
24
27
  subject(:instance) { MyTempResource.new( foo:'foo', bar:'bar' ) }
25
28
 
26
29
  describe '#struct_key' do
27
- subject { instance.struct_key }
30
+ subject { super().struct_key }
28
31
 
29
- it 'should return a valid, unique Ruby constant name' do
30
- subject.should == 'MyTempResourceFreneticResourceStruct'
32
+ it 'returns a valid, unique Ruby constant name' do
33
+ expect(subject).to eq 'MyTempResourceFreneticResourceStruct'
31
34
  end
32
35
  end
33
36
 
34
37
  describe '#signature' do
35
- subject { instance.signature }
38
+ subject { super().signature }
36
39
 
37
- it 'should return a unique and predictable key' do
38
- subject.should == 'barfoo'
40
+ it 'returns a unique and predictable key' do
41
+ expect(subject).to eq 'barfoo'
39
42
  end
40
43
  end
41
44
 
42
45
  describe '#structure' do
43
- subject { instance.structure }
46
+ subject { super().structure }
44
47
 
45
48
  context 'with no previously defined signature' do
46
49
  before do
47
- instance.stub( :structure_expired? ).and_return true
50
+ allow(instance).to receive(:structure_expired?).and_return(true)
48
51
  end
49
52
 
50
- it 'should rebuild the structure' do
51
- instance.should_receive :rebuild_structure!
52
-
53
+ it 'rebuilds the structure' do
54
+ allow(instance).to receive(:rebuild_structure!).and_call_original
53
55
  subject
56
+ expect(instance).to have_received(:rebuild_structure!)
54
57
  end
55
58
  end
56
59
 
57
60
  context 'with a fresh signature' do
58
61
  before do
59
62
  instance.structure
60
-
61
- instance.stub( :structure_expired? ).and_return false
63
+ allow(instance).to receive(:structure_expired?).and_return(false)
62
64
  end
63
65
 
64
- it 'should not rebuild the structure' do
65
- instance.should_receive( :rebuild_structure! ).never
66
-
66
+ it 'does not rebuild the structure' do
67
+ allow(instance).to receive(:rebuild_structure!)
67
68
  subject
69
+ expect(instance).to_not have_received(:rebuild_structure!)
68
70
  end
69
71
  end
70
72
 
71
73
  context 'with an expired signature' do
72
74
  before do
73
75
  instance.structure
74
-
75
- instance.stub( :structure_expired? ).and_return true
76
+ allow(instance).to receive(:structure_expired?).and_return(true)
76
77
  end
77
78
 
78
- it 'should rebuild the structure' do
79
- instance.should_receive :rebuild_structure!
80
-
79
+ it 'rebuilds the structure' do
80
+ allow(instance).to receive(:rebuild_structure!)
81
81
  subject
82
+ expect(instance).to have_received(:rebuild_structure!)
82
83
  end
83
84
  end
84
85
  end
@@ -86,42 +87,40 @@ describe Frenetic::Structured do
86
87
  describe '#fetch_structure' do
87
88
  before { instance.structure }
88
89
 
89
- subject { instance.fetch_structure }
90
+ subject { super().fetch_structure }
90
91
 
91
- it "should return the resource's Struct class" do
92
- subject.should == Struct::MyTempResourceFreneticResourceStruct
92
+ it 'returns the Struct class of the resource' do
93
+ expect(subject).to eq Struct::MyTempResourceFreneticResourceStruct
93
94
  end
94
95
  end
95
96
 
96
97
  describe '#rebuild_structure!' do
97
98
  before{ instance.structure }
98
99
 
99
- subject { instance.rebuild_structure! }
100
-
101
- it 'should destroy the previous Struct' do
102
- instance.should_receive(:destroy_structure!).and_call_original
100
+ subject { super().rebuild_structure! }
103
101
 
102
+ it 'destroys the previous Struct' do
103
+ allow(instance).to receive(:destroy_structure!).and_call_original
104
104
  subject
105
+ expect(instance).to have_received(:destroy_structure!)
105
106
  end
106
107
 
107
- it "should add cache the resource's signature" do
108
- sigs = described_class.class_variable_get('@@signatures')
109
-
110
- sigs.should include
108
+ it 'caches the signature of the resource' do
109
+ subject
110
+ expect(signatures).to include signature
111
111
  end
112
112
 
113
- it "should build the resource's Struct" do
113
+ it 'builds the Struct resource' do
114
114
  subject
115
-
116
- instance.fetch_structure.members.should == [:foo, :bar]
115
+ expect(instance.fetch_structure.members).to eq [:foo, :bar]
117
116
  end
118
117
  end
119
118
 
120
119
  describe '#structure_expired?' do
121
- subject { instance.structure_expired? }
120
+ subject { super().structure_expired? }
122
121
 
123
122
  before do
124
- instance.stub( :signature ).and_return new_sig
123
+ allow(instance).to receive(:signature).and_return(new_sig)
125
124
  described_class.class_variable_set '@@signatures', {
126
125
  'MyTempResourceFreneticResourceStruct' => old_sig
127
126
  }
@@ -131,84 +130,80 @@ describe Frenetic::Structured do
131
130
  let(:old_sig) { 'fresh' }
132
131
  let(:new_sig) { 'fresh' }
133
132
 
134
- it { should be_false }
133
+ it 'return FALSE' do
134
+ expect(subject).to eq false
135
+ end
135
136
  end
136
137
 
137
138
  context 'with no predefined signature' do
138
139
  let(:old_sig) { nil }
139
140
  let(:new_sig) { 'new' }
140
141
 
141
- it { should be_true }
142
+ it 'returns TRUE' do
143
+ expect(subject).to eq true
144
+ end
142
145
  end
143
146
 
144
147
  context 'with an expired signature' do
145
148
  let(:old_sig) { 'old' }
146
149
  let(:new_sig) { 'new' }
147
150
 
148
- it { should be_true }
151
+ it 'returns TRUE' do
152
+ expect(subject).to eq true
153
+ end
149
154
  end
150
155
  end
151
156
 
152
157
  describe '#structure_defined?' do
153
- subject { instance.structure_defined? }
154
-
155
158
  let(:consts) { Struct.constants || [] }
156
159
 
157
160
  before do
158
- Struct.stub(:constants).and_return consts
161
+ allow(Struct).to receive(:constants).and_return(consts)
159
162
  end
160
163
 
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
164
+ subject { super().structure_defined? }
167
165
 
166
+ it 'checks if the structure is defined' do
167
+ allow(instance).to receive(:struct_key).and_return('Foo')
168
+ allow(consts).to receive(:include?)
168
169
  subject
170
+ expect(consts).to have_received(:include?).with(:Foo)
169
171
  end
170
172
  end
171
173
 
172
174
  describe '#destroy_structure!' do
173
175
  before { instance.structure }
174
176
 
175
- subject { instance.destroy_structure! }
177
+ subject { super().destroy_structure! }
176
178
 
177
179
  context 'with an undefined structure' do
178
180
  before { instance.destroy_structure! }
179
181
 
180
- it "should not attempt to remove the structure's constant" do
181
- Struct.should_receive( :remove_const ).never
182
-
182
+ it 'does not attempt to remove the constant from the Struct' do
183
+ allow(Struct).to receive(:remove_const)
183
184
  subject
185
+ expect(Struct).to_not have_received(:remove_const)
184
186
  end
185
187
 
186
- it 'should not remove the signature from the cache' do
187
- described_class.class_variable_get('@@signatures').should_receive( :delete ).never
188
-
188
+ it 'does not remove the signature from the cache' do
189
+ allow(signatures).to receive(:delete)
189
190
  subject
191
+ expect(signatures).to_not receive(:delete)
190
192
  end
191
193
  end
192
194
 
193
195
  context 'with an predefined structure' do
194
- it 'should remove the constant' do
195
- Struct.constants.should include instance.struct_key.to_sym
196
-
196
+ it 'removes the constant' do
197
+ expect(Struct.constants).to include instance.struct_key.to_sym
197
198
  subject
198
-
199
- Struct.constants.should_not include instance.struct_key.to_sym
199
+ expect(Struct.constants).to_not include instance.struct_key.to_sym
200
200
  end
201
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
-
202
+ it 'removes the signature from the cache' do
203
+ expect(signatures).to include signature
207
204
  subject
208
-
209
- described_class.class_variable_get('@@signatures').should_not include signature
205
+ expect(signatures).to_not include signature
210
206
  end
211
207
  end
212
208
  end
213
-
214
209
  end