azeroth 0.6.4 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,18 +9,11 @@ module Azeroth
9
9
  module ClassMethods
10
10
  # Adds resource methods for resource
11
11
  #
12
- # @param name [String, Symbol] Name of the resource
13
- # @param options [Hash] resource building options
14
- # @option options only [Array<Symbol,String>] List of
15
- # actions to be built
16
- # @option options except [Array<Symbol,String>] List of
17
- # actions to not to be built
18
- # @option options decorator [Azeroth::Decorator,TrueClass,FalseClass]
19
- # Decorator class or flag allowing/disallowing decorators
12
+ # @param (see Resourceable.resource_for)
13
+ # @option (see Resourceable.resource_for)
14
+ # @return (see Resourceable.resource_for)
20
15
  #
21
- # @return [Array<MethodDefinition>] list of methods created
22
- #
23
- # @see Options
16
+ # @see (see Resourceable.resource_for)
24
17
  #
25
18
  # @example Controller without delete
26
19
  # class DocumentsController < ApplicationController
@@ -124,6 +117,33 @@ module Azeroth
124
117
  # # name: 'Nintendo'
125
118
  # # }
126
119
  # }
120
+ #
121
+ # @example Controller with before_save
122
+ # class PokemonsController < ApplicationController
123
+ # include Azeroth::Resourceable
124
+ #
125
+ # resource_for :pokemon,
126
+ # only: %i[create update],
127
+ # before_save: :set_favorite
128
+ #
129
+ # private
130
+ #
131
+ # def set_favorite
132
+ # pokemon.favorite = true
133
+ # end
134
+ #
135
+ # def pokemons
136
+ # master.pokemons
137
+ # end
138
+ #
139
+ # def master
140
+ # @master ||= PokemonMaster.find(master_id)
141
+ # end
142
+ #
143
+ # def master_id
144
+ # params.require(:pokemon_master_id)
145
+ # end
146
+ # end
127
147
  def resource_for(name, **options)
128
148
  Builder.new(
129
149
  self, name, Azeroth::Options.new(options)
@@ -62,13 +62,14 @@ module Azeroth
62
62
 
63
63
  def route_code(route)
64
64
  model_interface = model
65
+ options_object = options
65
66
  handler_class = Azeroth::RequestHandler.const_get(
66
67
  route.to_s.capitalize
67
68
  )
68
69
 
69
70
  proc do
70
71
  handler_class.new(
71
- self, model_interface
72
+ self, model_interface, options_object
72
73
  ).process
73
74
  end
74
75
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Azeroth
4
- VERSION = '0.6.4'
4
+ VERSION = '0.7.3'
5
5
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe PokemonMastersController do
6
+ describe 'POST create' do
7
+ let(:parameters) do
8
+ {
9
+ format: :json,
10
+ pokemon_master: {
11
+ first_name: 'Ash'
12
+ }
13
+ }
14
+ end
15
+
16
+ it 'creates pokemon master' do
17
+ expect { post :create, params: parameters }
18
+ .to change(PokemonMaster, :count)
19
+ .by(1)
20
+ end
21
+
22
+ it 'updates pokemon master age' do
23
+ post :create, params: parameters
24
+
25
+ expect(PokemonMaster.last.age).to eq(10)
26
+ end
27
+ end
28
+
29
+ describe 'POST update' do
30
+ let(:master) do
31
+ create(:pokemon_master, age: 20, last_name: nil)
32
+ end
33
+
34
+ let(:parameters) do
35
+ {
36
+ id: master.id,
37
+ format: :json,
38
+ pokemon_master: { last_name: 'Joe' }
39
+ }
40
+ end
41
+
42
+ it 'updates pokemon master' do
43
+ expect { post :update, params: parameters }
44
+ .to change { master.reload.last_name }
45
+ .from(nil)
46
+ .to('Joe')
47
+ end
48
+
49
+ it 'updates pokemon master age' do
50
+ expect { post :update, params: parameters }
51
+ .to change { master.reload.age }
52
+ .from(20)
53
+ .to(10)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe PokemonsController do
6
+ let(:master) { create(:pokemon_master) }
7
+
8
+ describe 'POST create' do
9
+ let(:parameters) do
10
+ {
11
+ pokemon_master_id: master.id,
12
+ format: :json,
13
+ pokemon: { name: 'Bulbasaur' }
14
+ }
15
+ end
16
+
17
+ it 'creates pokemon' do
18
+ expect { post :create, params: parameters }
19
+ .to change(Pokemon, :count)
20
+ .by(1)
21
+ end
22
+
23
+ it 'updates pokemon to be favorite' do
24
+ expect { post :create, params: parameters }
25
+ .to change { master.reload.favorite_pokemon }
26
+ .from(nil)
27
+ end
28
+ end
29
+
30
+ describe 'POST update' do
31
+ let(:pokemon) { create(:pokemon) }
32
+ let(:master) { pokemon.pokemon_master }
33
+
34
+ let(:parameters) do
35
+ {
36
+ pokemon_master_id: master.id,
37
+ id: pokemon.id,
38
+ format: :json,
39
+ pokemon: { name: 'Butterfree' }
40
+ }
41
+ end
42
+
43
+ it 'updates pokemon' do
44
+ expect { post :update, params: parameters }
45
+ .to change { pokemon.reload.name }
46
+ .from('Bulbasaur')
47
+ .to('Butterfree')
48
+ end
49
+
50
+ it 'updates pokemon to be favorite' do
51
+ expect { post :update, params: parameters }
52
+ .to change { pokemon.reload.favorite }
53
+ .from(nil)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PokemonMastersController < ApplicationController
4
+ include Azeroth::Resourceable
5
+
6
+ resource_for :pokemon_master,
7
+ only: %i[create update],
8
+ before_save: proc { pokemon_master.age = 10 }
9
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PokemonsController < ApplicationController
4
+ include Azeroth::Resourceable
5
+
6
+ resource_for :pokemon,
7
+ only: %i[create update],
8
+ before_save: :set_favorite
9
+
10
+ private
11
+
12
+ def set_favorite
13
+ pokemon.favorite = true
14
+ end
15
+
16
+ def pokemons
17
+ master.pokemons
18
+ end
19
+
20
+ def master
21
+ @master ||= PokemonMaster.find(master_id)
22
+ end
23
+
24
+ def master_id
25
+ params.require(:pokemon_master_id)
26
+ end
27
+ end
@@ -8,4 +8,8 @@ Rails.application.routes.draw do
8
8
  resources :publishers, only: %i[create index] do
9
9
  resources :games, except: :delete
10
10
  end
11
+
12
+ resources :pokemon_masters, only: %i[create update] do
13
+ resources :pokemons, only: %i[create update]
14
+ end
11
15
  end
@@ -227,5 +227,37 @@ describe Azeroth::Decorator::KeyValueExtractor do
227
227
  end
228
228
  end
229
229
  end
230
+
231
+ context 'when value is nil' do
232
+ context 'without decorator options' do
233
+ let(:object) { build(:document, name: nil) }
234
+
235
+ it 'returns nil for value' do
236
+ expect(extractor.as_json)
237
+ .to eq({ 'name' => nil })
238
+ end
239
+ end
240
+
241
+ context 'with decorator false' do
242
+ let(:object) { build(:document, name: nil) }
243
+ let(:options_hash) { { decorator: false } }
244
+
245
+ it 'returns nil for value' do
246
+ expect(extractor.as_json)
247
+ .to eq({ 'name' => nil })
248
+ end
249
+ end
250
+
251
+ context 'with decorator option' do
252
+ let(:object) { create(:pokemon_master) }
253
+ let(:options_hash) { { decorator: Pokemon::Decorator } }
254
+ let(:attribute) { :favorite_pokemon }
255
+
256
+ it 'returns nil for value' do
257
+ expect(extractor.as_json)
258
+ .to eq({ 'favorite_pokemon' => nil })
259
+ end
260
+ end
261
+ end
230
262
  end
231
263
  end
@@ -62,6 +62,12 @@ describe Azeroth::Decorator do
62
62
  end
63
63
 
64
64
  describe '#as_json' do
65
+ context 'when object is nil' do
66
+ let(:object) { nil }
67
+
68
+ it { expect(decorator.as_json).to be_nil }
69
+ end
70
+
65
71
  context 'when object is just a model' do
66
72
  let(:expected_json) do
67
73
  {
@@ -166,7 +172,7 @@ describe Azeroth::Decorator do
166
172
  end
167
173
  end
168
174
 
169
- context 'with decotator for model with validation' do
175
+ context 'with decorator for model with validation' do
170
176
  subject(:decorator) do
171
177
  Document::DecoratorWithError.new(object)
172
178
  end
@@ -255,6 +261,26 @@ describe Azeroth::Decorator do
255
261
  expect(decorator.as_json).to eq(expected_json)
256
262
  end
257
263
  end
264
+
265
+ context 'with nil value and defined decorator' do
266
+ subject(:decorator) do
267
+ Factory::DecoratorWithProduct.new(factory)
268
+ end
269
+
270
+ let(:factory) { create(:factory) }
271
+
272
+ let(:expected_json) do
273
+ {
274
+ name: factory.name,
275
+ main_product: nil,
276
+ products: []
277
+ }.deep_stringify_keys
278
+ end
279
+
280
+ it 'exposes relation' do
281
+ expect(decorator.as_json).to eq(expected_json)
282
+ end
283
+ end
258
284
  end
259
285
  end
260
286
 
@@ -262,7 +288,7 @@ describe Azeroth::Decorator do
262
288
  subject(:decorator) { decorator_class.new(object) }
263
289
 
264
290
  let(:decorator_class) { Class.new(described_class) }
265
- let(:model) { build(:dummy_model) }
291
+ let(:model) { build(:dummy_model) }
266
292
 
267
293
  it 'delegates methods to object' do
268
294
  expect(decorator.first_name).not_to be_nil
@@ -23,6 +23,172 @@ describe Azeroth::RequestHandler::Create do
23
23
  .by(1)
24
24
  end
25
25
  end
26
+
27
+ context 'with before_save block option' do
28
+ it_behaves_like 'a request handler', status: :created do
29
+ let(:block) do
30
+ value = 10
31
+ proc do
32
+ document.reference = "X-MAGIC-#{value}"
33
+ end
34
+ end
35
+
36
+ let(:options_hash) do
37
+ {
38
+ before_save: block
39
+ }
40
+ end
41
+
42
+ let(:extra_params) do
43
+ {
44
+ document: {
45
+ name: 'My Document'
46
+ }
47
+ }
48
+ end
49
+
50
+ let(:expected_json) do
51
+ {
52
+ 'name' => 'My Document',
53
+ 'reference' => 'X-MAGIC-10'
54
+ }
55
+ end
56
+
57
+ it 'creates entry' do
58
+ expect { handler.process }
59
+ .to change(Document, :count)
60
+ .by(1)
61
+ end
62
+
63
+ it 'changes entry before saving' do
64
+ handler.process
65
+
66
+ expect(Document.last.reference)
67
+ .to eq('X-MAGIC-10')
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'with before_save symbol option' do
73
+ it_behaves_like 'a request handler', status: :created do
74
+ let(:options_hash) do
75
+ {
76
+ before_save: :add_magic_reference
77
+ }
78
+ end
79
+
80
+ let(:extra_params) do
81
+ {
82
+ document: {
83
+ name: 'My Document'
84
+ }
85
+ }
86
+ end
87
+
88
+ let(:expected_json) do
89
+ {
90
+ 'name' => 'My Document',
91
+ 'reference' => 'X-MAGIC-15'
92
+ }
93
+ end
94
+
95
+ it 'creates entry' do
96
+ expect { handler.process }
97
+ .to change(Document, :count)
98
+ .by(1)
99
+ end
100
+
101
+ it 'changes entry before saving' do
102
+ handler.process
103
+
104
+ expect(Document.last.reference)
105
+ .to eq('X-MAGIC-15')
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'with build_with as symbol' do
112
+ it_behaves_like 'a request handler', status: :created do
113
+ let(:options_hash) do
114
+ {
115
+ build_with: :build_magic_document
116
+ }
117
+ end
118
+
119
+ let(:extra_params) do
120
+ {
121
+ document: {
122
+ name: 'My Document'
123
+ }
124
+ }
125
+ end
126
+
127
+ let(:expected_json) do
128
+ {
129
+ 'name' => 'My Document',
130
+ 'reference' => 'X-MAGIC-15'
131
+ }
132
+ end
133
+
134
+ it 'creates entry' do
135
+ expect { handler.process }
136
+ .to change(Document, :count)
137
+ .by(1)
138
+ end
139
+
140
+ it 'builds entity with custom method' do
141
+ handler.process
142
+
143
+ expect(Document.last.reference)
144
+ .to eq('X-MAGIC-15')
145
+ end
146
+ end
147
+ end
148
+
149
+ context 'with build_with as block' do
150
+ it_behaves_like 'a request handler', status: :created do
151
+ let(:block) do
152
+ proc do
153
+ documents.where(reference: 'X-MAGIC-20')
154
+ .build(document_params)
155
+ end
156
+ end
157
+
158
+ let(:options_hash) do
159
+ {
160
+ build_with: block
161
+ }
162
+ end
163
+
164
+ let(:extra_params) do
165
+ {
166
+ document: {
167
+ name: 'My Document'
168
+ }
169
+ }
170
+ end
171
+
172
+ let(:expected_json) do
173
+ {
174
+ 'name' => 'My Document',
175
+ 'reference' => 'X-MAGIC-20'
176
+ }
177
+ end
178
+
179
+ it 'creates entry' do
180
+ expect { handler.process }
181
+ .to change(Document, :count)
182
+ .by(1)
183
+ end
184
+
185
+ it 'builds entity with custom method' do
186
+ handler.process
187
+
188
+ expect(Document.last.reference)
189
+ .to eq('X-MAGIC-20')
190
+ end
191
+ end
26
192
  end
27
193
 
28
194
  context 'when payload is invalid' do