frenetic 0.0.20.alpha.6 → 1.0.0.alpha.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.
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