acfs 1.3.3 → 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +339 -0
  3. data/LICENSE +22 -0
  4. data/README.md +335 -0
  5. data/acfs.gemspec +46 -0
  6. data/lib/acfs.rb +51 -0
  7. data/lib/acfs/adapter/base.rb +24 -0
  8. data/lib/acfs/adapter/typhoeus.rb +69 -0
  9. data/lib/acfs/collection.rb +28 -0
  10. data/lib/acfs/collections/paginatable.rb +76 -0
  11. data/lib/acfs/configuration.rb +120 -0
  12. data/lib/acfs/errors.rb +127 -0
  13. data/lib/acfs/global.rb +101 -0
  14. data/lib/acfs/location.rb +82 -0
  15. data/lib/acfs/middleware/base.rb +24 -0
  16. data/lib/acfs/middleware/json.rb +29 -0
  17. data/lib/acfs/middleware/logger.rb +25 -0
  18. data/lib/acfs/middleware/msgpack.rb +32 -0
  19. data/lib/acfs/middleware/print.rb +23 -0
  20. data/lib/acfs/middleware/serializer.rb +41 -0
  21. data/lib/acfs/operation.rb +83 -0
  22. data/lib/acfs/request.rb +39 -0
  23. data/lib/acfs/request/callbacks.rb +54 -0
  24. data/lib/acfs/resource.rb +39 -0
  25. data/lib/acfs/resource/attributes.rb +269 -0
  26. data/lib/acfs/resource/attributes/base.rb +29 -0
  27. data/lib/acfs/resource/attributes/boolean.rb +39 -0
  28. data/lib/acfs/resource/attributes/date_time.rb +32 -0
  29. data/lib/acfs/resource/attributes/dict.rb +39 -0
  30. data/lib/acfs/resource/attributes/float.rb +33 -0
  31. data/lib/acfs/resource/attributes/integer.rb +29 -0
  32. data/lib/acfs/resource/attributes/list.rb +36 -0
  33. data/lib/acfs/resource/attributes/string.rb +26 -0
  34. data/lib/acfs/resource/attributes/uuid.rb +48 -0
  35. data/lib/acfs/resource/dirty.rb +37 -0
  36. data/lib/acfs/resource/initialization.rb +31 -0
  37. data/lib/acfs/resource/loadable.rb +35 -0
  38. data/lib/acfs/resource/locatable.rb +132 -0
  39. data/lib/acfs/resource/operational.rb +23 -0
  40. data/lib/acfs/resource/persistence.rb +260 -0
  41. data/lib/acfs/resource/query_methods.rb +266 -0
  42. data/lib/acfs/resource/service.rb +44 -0
  43. data/lib/acfs/resource/validation.rb +39 -0
  44. data/lib/acfs/response.rb +30 -0
  45. data/lib/acfs/response/formats.rb +27 -0
  46. data/lib/acfs/response/status.rb +33 -0
  47. data/lib/acfs/rspec.rb +13 -0
  48. data/lib/acfs/runner.rb +102 -0
  49. data/lib/acfs/service.rb +97 -0
  50. data/lib/acfs/service/middleware.rb +58 -0
  51. data/lib/acfs/service/middleware/stack.rb +65 -0
  52. data/lib/acfs/singleton_resource.rb +85 -0
  53. data/lib/acfs/stub.rb +194 -0
  54. data/lib/acfs/util.rb +22 -0
  55. data/lib/acfs/version.rb +16 -0
  56. data/lib/acfs/yard.rb +6 -0
  57. data/spec/acfs/adapter/typhoeus_spec.rb +55 -0
  58. data/spec/acfs/collection_spec.rb +157 -0
  59. data/spec/acfs/configuration_spec.rb +53 -0
  60. data/spec/acfs/global_spec.rb +140 -0
  61. data/spec/acfs/location_spec.rb +25 -0
  62. data/spec/acfs/middleware/json_spec.rb +65 -0
  63. data/spec/acfs/middleware/msgpack_spec.rb +62 -0
  64. data/spec/acfs/operation_spec.rb +12 -0
  65. data/spec/acfs/request/callbacks_spec.rb +48 -0
  66. data/spec/acfs/request_spec.rb +79 -0
  67. data/spec/acfs/resource/attributes/boolean_spec.rb +58 -0
  68. data/spec/acfs/resource/attributes/date_time_spec.rb +51 -0
  69. data/spec/acfs/resource/attributes/dict_spec.rb +77 -0
  70. data/spec/acfs/resource/attributes/float_spec.rb +61 -0
  71. data/spec/acfs/resource/attributes/integer_spec.rb +36 -0
  72. data/spec/acfs/resource/attributes/list_spec.rb +60 -0
  73. data/spec/acfs/resource/attributes/uuid_spec.rb +42 -0
  74. data/spec/acfs/resource/attributes_spec.rb +181 -0
  75. data/spec/acfs/resource/dirty_spec.rb +49 -0
  76. data/spec/acfs/resource/initialization_spec.rb +36 -0
  77. data/spec/acfs/resource/loadable_spec.rb +22 -0
  78. data/spec/acfs/resource/locatable_spec.rb +118 -0
  79. data/spec/acfs/resource/persistance_spec.rb +322 -0
  80. data/spec/acfs/resource/query_methods_spec.rb +548 -0
  81. data/spec/acfs/resource/validation_spec.rb +129 -0
  82. data/spec/acfs/response/formats_spec.rb +52 -0
  83. data/spec/acfs/response/status_spec.rb +71 -0
  84. data/spec/acfs/runner_spec.rb +95 -0
  85. data/spec/acfs/service/middleware_spec.rb +35 -0
  86. data/spec/acfs/service_spec.rb +48 -0
  87. data/spec/acfs/singleton_resource_spec.rb +17 -0
  88. data/spec/acfs/stub_spec.rb +345 -0
  89. data/spec/acfs_spec.rb +205 -0
  90. data/spec/fixtures/config.yml +14 -0
  91. data/spec/spec_helper.rb +43 -0
  92. data/spec/support/hash.rb +11 -0
  93. data/spec/support/response.rb +12 -0
  94. data/spec/support/service.rb +92 -0
  95. data/spec/support/shared/find_callbacks.rb +50 -0
  96. metadata +136 -3
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Acfs::Resource::Attributes::UUID do
6
+ let(:type) { Acfs::Resource::Attributes::UUID.new }
7
+
8
+ describe '#cast' do
9
+ subject { -> { type.cast(value) } }
10
+
11
+ context 'with nil' do
12
+ let(:value) { nil }
13
+ it { expect(subject.call).to eq nil }
14
+ end
15
+
16
+ context 'with empty string' do
17
+ let(:value) { '' }
18
+ it { expect(subject.call).to eq nil }
19
+ end
20
+
21
+ context 'with blank string' do
22
+ let(:value) { " \t" }
23
+ it { expect(subject.call).to eq nil }
24
+ end
25
+
26
+ context 'with string UUID' do
27
+ let(:value) { '450b7a40-94ad-11e3-baa8-0800200c9a66' }
28
+ it { expect(subject.call).to be_a String }
29
+ it { expect(subject.call).to eq value }
30
+ end
31
+
32
+ context 'with invalid string' do
33
+ let(:value) { 'invalid string' }
34
+ it { is_expected.to raise_error TypeError, /invalid UUID/i }
35
+ end
36
+
37
+ context 'with invalid UUID' do
38
+ let(:value) { 'xxxxxxxx-yyyy-11e3-baa8-0800200c9a66' }
39
+ it { is_expected.to raise_error TypeError, /invalid UUID/i }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Acfs::Resource::Attributes do
6
+ let(:model) { Class.new Acfs::Resource }
7
+ let(:submodel) { Class.new model }
8
+
9
+ describe '#initialize' do
10
+ before { model.attribute :name, :string, default: 'John' }
11
+
12
+ it 'should have attribute list' do
13
+ expect(model.new.attributes).to include(:name)
14
+ end
15
+
16
+ it 'should set default attributes' do
17
+ expect(model.new.name).to be == 'John'
18
+ end
19
+
20
+ context 'with dynamic default value' do
21
+ before do
22
+ model.attribute :name, :string, default: 'John'
23
+ model.attribute :mail, :string, default: -> { "#{name}@srv.tld" }
24
+ end
25
+
26
+ it 'should set dynamic default attributes' do
27
+ expect(model.new.mail).to be == 'John@srv.tld'
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#attributes' do
33
+ before do
34
+ model.attribute :name, :string, default: 'John'
35
+ model.attribute :age, :integer, default: 25
36
+ end
37
+
38
+ it 'should return hash of all attributes' do
39
+ expect(model.new.attributes).to eq(name: 'John', age: 25)
40
+ end
41
+ end
42
+
43
+ describe '#write_attributes' do
44
+ before do
45
+ model.attribute :name, :string, default: 'John'
46
+ model.attribute :age, :integer, default: 25
47
+ model.send :define_method, :name= do |name|
48
+ write_attribute :name, "The Great #{name}"
49
+ end
50
+ end
51
+ let(:args) { [params] }
52
+ let(:params) { {name: 'James'} }
53
+ let(:m) { model.new }
54
+ let(:action) { -> { m.write_attributes(*args) } }
55
+ subject { action }
56
+
57
+ it 'should update attributes' do
58
+ should change(m, :attributes)
59
+ .from(name: 'The Great John', age: 25)
60
+ .to(name: 'The Great James', age: 25)
61
+ end
62
+
63
+ context 'without non-hash params' do
64
+ let(:params) { 'James' }
65
+
66
+ it { should_not change(m, :attributes) }
67
+ its(:call) { should eq false }
68
+ end
69
+
70
+ context 'with unknown attributes' do
71
+ let(:params) { {name: 'James', born_at: 'today'} }
72
+
73
+ it { should_not raise_error }
74
+
75
+ it 'should update known attributes and store unknown' do
76
+ should change(m, :attributes)
77
+ .from(name: 'The Great John', age: 25)
78
+ .to(name: 'The Great James', age: 25, born_at: 'today')
79
+ end
80
+
81
+ context 'with unknown: :raise option' do
82
+ let(:args) { [params, {unknown: :raise}] }
83
+
84
+ it { should raise_error(ArgumentError, /unknown attribute/i) }
85
+
86
+ it do
87
+ expect do
88
+ begin
89
+ subject.call
90
+ rescue StandardError
91
+ true
92
+ end
93
+ end.to_not change(m, :attributes)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ describe '#_getter_' do
100
+ before { model.attribute :name, :string, default: 'John' }
101
+
102
+ it 'should return value' do
103
+ mo = model.new
104
+ mo.name = 'Paul'
105
+
106
+ expect(mo.name).to be == 'Paul'
107
+ end
108
+
109
+ it 'should return default value' do
110
+ expect(model.new.name).to be == 'John'
111
+ end
112
+ end
113
+
114
+ describe '#_setter_' do
115
+ before do
116
+ model.attribute :name, :string, default: 'John'
117
+ model.attribute :age, :integer, default: '25'
118
+ end
119
+
120
+ it 'should set value' do
121
+ o = model.new
122
+ o.name = 'Paul'
123
+
124
+ expect(o.name).to be == 'Paul'
125
+ end
126
+
127
+ it 'should update attributes hash' do
128
+ o = model.new
129
+ o.name = 'Johannes'
130
+
131
+ expect(o.attributes['name']).to be == 'Johannes'
132
+ end
133
+
134
+ it 'should cast values' do
135
+ o = model.new
136
+ o.age = '28'
137
+
138
+ expect(o.age).to be == 28
139
+ end
140
+ end
141
+
142
+ describe 'class' do
143
+ describe '#attributes' do
144
+ it 'should add an attribute to model attribute list' do
145
+ model.send :attribute, :name, :string
146
+
147
+ expect(model.attributes.symbolize_keys).to eq name: nil
148
+ end
149
+
150
+ it 'should accept a default value' do
151
+ model.send :attribute, :name, :string, default: 'John'
152
+
153
+ expect(model.attributes.symbolize_keys).to eq name: 'John'
154
+ end
155
+
156
+ it 'should accept a symbolic type' do
157
+ model.send :attribute, :age, :integer, default: '12'
158
+
159
+ expect(model.attributes.symbolize_keys).to eq age: 12
160
+ end
161
+
162
+ it 'should accept a class type' do
163
+ model.send :attribute, :age, Acfs::Resource::Attributes::Integer,
164
+ default: '12'
165
+
166
+ expect(model.attributes.symbolize_keys).to eq age: 12
167
+ end
168
+
169
+ context 'on inherited resources' do
170
+ before do
171
+ model.attribute :age, :integer, default: 5
172
+ submodel.attribute :born_at, :date_time
173
+ end
174
+
175
+ it 'includes superclass attributes' do
176
+ expect(submodel.attributes.keys).to match_array %w[age born_at]
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Acfs::Resource::Dirty do
6
+ let(:model) { MyUser.new }
7
+ before do
8
+ stub_request(:get, 'http://users.example.org/users/1')
9
+ .to_return response id: 1, name: 'Anon', age: 12
10
+ stub_request(:post, 'http://users.example.org/users')
11
+ .to_return response id: 5, name: 'dhh', age: 12
12
+ end
13
+
14
+ it 'includes ActiveModel::Dirty' do
15
+ model.is_a? ActiveModel::Dirty
16
+ end
17
+
18
+ describe '#changed?' do
19
+ context 'after attribute change' do
20
+ let(:user) { MyUser.new name: 'dhh' }
21
+
22
+ it { expect(user).to be_changed }
23
+
24
+ context 'and saving' do
25
+ before { user.save }
26
+ it { expect(user).to_not be_changed }
27
+ end
28
+ end
29
+
30
+ context 'after model load' do
31
+ let(:user) { MyUser.find 1 }
32
+ before { user && Acfs.run }
33
+
34
+ it { expect(user).to_not be_changed }
35
+ end
36
+
37
+ context 'after model new without attrs' do
38
+ let(:user) { MyUser.new }
39
+
40
+ it { expect(user).to_not be_changed }
41
+ end
42
+
43
+ context 'after model new with attrs' do
44
+ let(:user) { MyUser.new name: 'Uschi' }
45
+
46
+ it { expect(user).to be_changed }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Acfs::Resource::Initialization' do
6
+ let(:model) do
7
+ Class.new(Acfs::Resource).tap do |c|
8
+ c.class_eval do
9
+ attr_accessor :name
10
+ attr_reader :age
11
+
12
+ private
13
+
14
+ attr_writer :age
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#initialize' do
20
+ it 'should allow to set attributes with initializer' do
21
+ m = model.new name: 'John'
22
+ expect(m.name).to eq 'John'
23
+ end
24
+
25
+ it 'should raise error when attributes with private setters are given' do
26
+ expect { model.new age: 25 }.to raise_error(NoMethodError)
27
+ end
28
+ end
29
+
30
+ describe '#persisted?' do
31
+ subject { model.new.persisted? }
32
+ it 'should be false' do
33
+ should be false
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Acfs::Resource::Loadable do
6
+ let(:model) { MyUser.find 1 }
7
+ before do
8
+ stub_request(:get, 'http://users.example.org/users/1')
9
+ .to_return response id: 1, name: 'Anon', age: 12
10
+ end
11
+
12
+ describe '#loaded?' do
13
+ context 'before Acfs#run' do
14
+ it { expect(model).to_not be_loaded }
15
+ end
16
+
17
+ context 'afer Acfs#run' do
18
+ before { model && Acfs.run }
19
+ it { expect(model).to be_loaded }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Acfs::Resource::Locatable do
6
+ let(:model) { MyUser }
7
+ before do
8
+ stub_request(:get, 'http://users.example.org/users/1')
9
+ .to_return response id: 1, name: 'Anon', age: 12
10
+ stub_request(:get, 'http://users.example.org/users/1/profile')
11
+ .to_return response user_id: 2, twitter_handle: '@anon'
12
+ end
13
+
14
+ describe '.url' do
15
+ it 'should return URL' do
16
+ expect(model.url).to be == 'http://users.example.org/users'
17
+ end
18
+
19
+ it 'should return URL with id path part if specified' do
20
+ expect(model.url(5)).to be == 'http://users.example.org/users/5'
21
+ end
22
+
23
+ context 'with attribute in path' do
24
+ let(:model) { Profile }
25
+
26
+ it 'should replace placeholder' do
27
+ expect(model.url(user_id: 1))
28
+ .to eq 'http://users.example.org/users/1/profile'
29
+ end
30
+
31
+ context 'without attributes' do
32
+ it 'should raise an error if attribute is missing' do
33
+ expect { model.url }.to raise_error ArgumentError
34
+ end
35
+ end
36
+ end
37
+
38
+ describe 'custom paths' do
39
+ let(:model) { Session }
40
+ let(:location) { Session.location action: action }
41
+ subject { location }
42
+
43
+ context ':list location' do
44
+ let(:action) { :list }
45
+
46
+ its(:raw_uri) do
47
+ should eq 'http://users.example.org/users/:user_id/sessions'
48
+ end
49
+ end
50
+
51
+ context ':create location' do
52
+ let(:action) { :create }
53
+ its(:raw_uri) { should eq 'http://users.example.org/sessions' }
54
+ end
55
+
56
+ context ':read location' do
57
+ let(:action) { :read }
58
+ its(:raw_uri) { should eq 'http://users.example.org/sessions/:id' }
59
+ end
60
+
61
+ context ':update location' do
62
+ let(:action) { :update }
63
+ its(:raw_uri) do
64
+ expect { subject }.to raise_error ArgumentError, /update.*disabled/
65
+ end
66
+ end
67
+
68
+ context ':delete location' do
69
+ let(:action) { :delete }
70
+ its(:raw_uri) do
71
+ should eq 'http://users.example.org/users/:user_id/sessions/del/:id'
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#url' do
78
+ context 'new resource' do
79
+ let(:m) { model.new }
80
+
81
+ it 'should return nil' do
82
+ expect(m.url).to be_nil
83
+ end
84
+
85
+ context 'new resource with id' do
86
+ let(:m) { model.new id: 475 }
87
+
88
+ it 'should return resource URL' do
89
+ expect(m.url).to eq 'http://users.example.org/users/475'
90
+ end
91
+ end
92
+
93
+ context 'with attribute in path' do
94
+ it 'should return nil' do
95
+ expect(m.url).to be_nil
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'loaded resource' do
101
+ let(:m) { model.find 1 }
102
+ before { m && Acfs.run }
103
+
104
+ it "should return resource's URL" do
105
+ expect(m.url).to eq 'http://users.example.org/users/1'
106
+ end
107
+
108
+ context 'with attribute in path' do
109
+ let(:model) { Profile }
110
+ let(:m) { model.find user_id: 1 }
111
+
112
+ it "should return resource's URL" do
113
+ expect(m.url).to eq 'http://users.example.org/users/2/profile'
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end