acfs 1.3.3 → 1.3.4

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 (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