azeroth 0.6.3 → 0.7.2

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +37 -2
  3. data/.rubocop.yml +11 -0
  4. data/Dockerfile +2 -2
  5. data/README.md +164 -1
  6. data/azeroth.gemspec +6 -5
  7. data/lib/azeroth.rb +1 -0
  8. data/lib/azeroth/decorator.rb +123 -0
  9. data/lib/azeroth/decorator/key_value_extractor.rb +2 -2
  10. data/lib/azeroth/decorator/options.rb +3 -3
  11. data/lib/azeroth/model.rb +3 -1
  12. data/lib/azeroth/options.rb +38 -1
  13. data/lib/azeroth/request_handler.rb +39 -2
  14. data/lib/azeroth/request_handler/create.rb +31 -5
  15. data/lib/azeroth/request_handler/update.rb +3 -3
  16. data/lib/azeroth/resourceable.rb +27 -0
  17. data/lib/azeroth/resourceable/class_methods.rb +120 -6
  18. data/lib/azeroth/routes_builder.rb +2 -1
  19. data/lib/azeroth/version.rb +1 -1
  20. data/spec/controllers/pokemon_masters_controller_spec.rb +56 -0
  21. data/spec/controllers/pokemons_controller_spec.rb +56 -0
  22. data/spec/dummy/app/controllers/games_controller.rb +22 -0
  23. data/spec/dummy/app/controllers/pokemon_masters_controller.rb +9 -0
  24. data/spec/dummy/app/controllers/pokemons_controller.rb +27 -0
  25. data/spec/dummy/app/controllers/publishers_controller.rb +8 -0
  26. data/spec/dummy/app/models/game.rb +5 -0
  27. data/spec/dummy/app/models/game/decorator.rb +9 -0
  28. data/spec/dummy/app/models/name_decorator.rb +5 -0
  29. data/spec/dummy/app/models/pokemon.rb +8 -0
  30. data/spec/dummy/app/models/pokemon/decorator.rb +16 -0
  31. data/spec/dummy/app/models/pokemon/favorite_decorator.rb +7 -0
  32. data/spec/dummy/app/models/pokemon_master.rb +7 -0
  33. data/spec/dummy/app/models/pokemon_master/decorator.rb +17 -0
  34. data/spec/dummy/app/models/publisher.rb +5 -0
  35. data/spec/dummy/config/routes.rb +8 -0
  36. data/spec/dummy/db/schema.rb +26 -0
  37. data/spec/integration/readme/azeroth/decorator_spec.rb +48 -0
  38. data/spec/integration/readme/controllers/games_controller_spec.rb +42 -0
  39. data/spec/integration/yard/azeroth/decorator_spec.rb +58 -17
  40. data/spec/integration/yard/controllers/games_controller_spec.rb +42 -0
  41. data/spec/lib/azeroth/decorator/key_value_extractor_spec.rb +32 -0
  42. data/spec/lib/azeroth/decorator_spec.rb +28 -2
  43. data/spec/lib/azeroth/request_handler/create_spec.rb +166 -0
  44. data/spec/lib/azeroth/request_handler/update_spec.rb +91 -0
  45. data/spec/lib/azeroth/request_handler_spec.rb +3 -1
  46. data/spec/support/app/controllers/request_handler_controller.rb +12 -1
  47. data/spec/support/factories/game.rb +8 -0
  48. data/spec/support/factories/pokemon.rb +8 -0
  49. data/spec/support/factories/pokemon_master.rb +9 -0
  50. data/spec/support/factories/publisher.rb +7 -0
  51. data/spec/support/shared_examples/request_handler.rb +5 -2
  52. metadata +70 -12
@@ -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,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class GamesController < ApplicationController
4
+ include Azeroth::Resourceable
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ resource_for :game, except: :delete
8
+
9
+ private
10
+
11
+ def games
12
+ publisher.games
13
+ end
14
+
15
+ def publisher
16
+ @publisher ||= Publisher.find(publisher_id)
17
+ end
18
+
19
+ def publisher_id
20
+ params.require(:publisher_id)
21
+ end
22
+ 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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PublishersController < ApplicationController
4
+ include Azeroth::Resourceable
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ resource_for :publisher, only: %i[create index]
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Game < ActiveRecord::Base
4
+ belongs_to :publisher
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Game < ActiveRecord::Base
4
+ class Decorator < Azeroth::Decorator
5
+ expose :id
6
+ expose :name
7
+ expose :publisher, decorator: NameDecorator
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NameDecorator < Azeroth::Decorator
4
+ expose :name
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pokemon < ActiveRecord::Base
4
+ belongs_to :pokemon_master
5
+ has_one :previous_form,
6
+ class_name: 'Pokemon',
7
+ foreign_key: :previous_form_id
8
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pokemon
4
+ class Decorator < Azeroth::Decorator
5
+ expose :name
6
+ expose :previous_form_name, as: :evolution_of, if: :evolution?
7
+
8
+ def evolution?
9
+ previous_form
10
+ end
11
+
12
+ def previous_form_name
13
+ previous_form.name
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pokemon
4
+ class FavoriteDecorator < Pokemon::Decorator
5
+ expose :nickname
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PokemonMaster < ActiveRecord::Base
4
+ has_one :favorite_pokemon, -> { where(favorite: true) },
5
+ class_name: 'Pokemon'
6
+ has_many :pokemons
7
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PokemonMaster
4
+ class Decorator < Azeroth::Decorator
5
+ expose :name
6
+ expose :age
7
+ expose :favorite_pokemon, decorator: Pokemon::FavoriteDecorator
8
+ expose :pokemons
9
+
10
+ def name
11
+ [
12
+ first_name,
13
+ last_name
14
+ ].compact.join(' ')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Publisher < ActiveRecord::Base
4
+ has_many :games
5
+ end
@@ -4,4 +4,12 @@ Rails.application.routes.draw do
4
4
  resources :documents
5
5
  resources :public_documents, controller: :index_documents
6
6
  resources :create_documents, controller: :documents_with_error
7
+
8
+ resources :publishers, only: %i[create index] do
9
+ resources :games, except: :delete
10
+ end
11
+
12
+ resources :pokemon_masters, only: %i[create update] do
13
+ resources :pokemons, only: %i[create update]
14
+ end
7
15
  end
@@ -22,4 +22,30 @@ ActiveRecord::Schema.define do
22
22
  t.integer :factory_id
23
23
  t.string :name
24
24
  end
25
+
26
+ create_table :publishers, force: true do |t|
27
+ t.string :name
28
+ end
29
+
30
+ create_table :games, force: true do |t|
31
+ t.string :name
32
+ t.integer :publisher_id
33
+ end
34
+
35
+ create_table :pokemon_masters, force: true do |t|
36
+ t.string :first_name, null: false
37
+ t.string :last_name
38
+ t.integer :age, null: false
39
+ end
40
+
41
+ create_table :pokemons, force: true do |t|
42
+ t.string :name, null: false
43
+ t.string :nickname
44
+ t.integer :pokemon_master_id
45
+ t.boolean :favorite
46
+ t.integer :previous_form_id
47
+ t.index %i[pokemon_master_id favorite], unique: true
48
+ end
49
+
50
+ add_foreign_key 'pokemons', 'pokemon_masters'
25
51
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Azeroth::Decorator do
6
+ describe 'readme' do
7
+ it 'decorates model' do
8
+ master = PokemonMaster.create(
9
+ first_name: 'Ash',
10
+ last_name: 'Ketchum',
11
+ age: 10
12
+ )
13
+
14
+ master.create_favorite_pokemon(
15
+ name: 'pikachu',
16
+ nickname: 'Pikachu'
17
+ )
18
+
19
+ metapod = Pokemon.create(name: :metapod)
20
+
21
+ master.pokemons.create(
22
+ name: 'butterfree', previous_form: metapod
23
+ )
24
+ master.pokemons.create(name: 'squirtle')
25
+
26
+ decorator = PokemonMaster::Decorator.new(master)
27
+
28
+ expect(decorator.as_json).to eq(
29
+ {
30
+ 'age' => 10,
31
+ 'name' => 'Ash Ketchum',
32
+ 'favorite_pokemon' => {
33
+ 'name' => 'pikachu',
34
+ 'nickname' => 'Pikachu'
35
+ },
36
+ 'pokemons' => [{
37
+ 'name' => 'butterfree',
38
+ 'evolution_of' => 'metapod'
39
+ }, {
40
+ 'name' => 'squirtle'
41
+ }, {
42
+ 'name' => 'pikachu'
43
+ }]
44
+ }
45
+ )
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe GamesController, controller: true do
6
+ describe 'readme' do
7
+ describe 'POST create' do
8
+ it 'create game' do
9
+ post '/publishers.json', params: {
10
+ publisher: {
11
+ name: 'Nintendo'
12
+ }
13
+ }
14
+
15
+ publisher = JSON.parse(response.body)
16
+ expect(publisher)
17
+ .to eq({
18
+ 'id' => publisher['id'],
19
+ 'name' => 'Nintendo'
20
+ })
21
+
22
+ publisher = Publisher.last
23
+ post "/publishers/#{publisher['id']}/games.json", params: {
24
+ game: {
25
+ name: 'Pokemon'
26
+ }
27
+ }
28
+
29
+ game = Game.last
30
+
31
+ expect(JSON.parse(response.body))
32
+ .to eq({
33
+ 'id' => game.id,
34
+ 'name' => 'Pokemon',
35
+ 'publisher' => {
36
+ 'name' => 'Nintendo'
37
+ }
38
+ })
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,24 +1,65 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe Azeroth::Decorator do
4
- subject(:decorator) { DummyModel::Decorator.new(object) }
5
-
6
- let(:object) do
7
- DummyModel.new(
8
- id: 100,
9
- first_name: 'John',
10
- last_name: 'Wick',
11
- favorite_pokemon: 'Arcanine'
12
- )
13
- end
4
+ describe 'yard' do
5
+ describe '#as_json' do
6
+ it 'simple example' do
7
+ object = DummyModel.new(
8
+ id: 100,
9
+ first_name: 'John',
10
+ last_name: 'Wick',
11
+ favorite_pokemon: 'Arcanine'
12
+ )
13
+
14
+ decorator = DummyModel::Decorator.new(object)
15
+
16
+ expect(decorator.as_json).to eq(
17
+ 'name' => 'John Wick',
18
+ 'age' => nil,
19
+ 'pokemon' => 'Arcanine'
20
+ )
21
+ end
22
+
23
+ it 'complete example' do
24
+ master = PokemonMaster.create(
25
+ first_name: 'Ash',
26
+ last_name: 'Ketchum',
27
+ age: 10
28
+ )
29
+
30
+ master.create_favorite_pokemon(
31
+ name: 'pikachu',
32
+ nickname: 'Pikachu'
33
+ )
34
+
35
+ metapod = Pokemon.create(name: :metapod)
36
+
37
+ master.pokemons.create(
38
+ name: 'butterfree', previous_form: metapod
39
+ )
40
+ master.pokemons.create(name: 'squirtle')
41
+
42
+ decorator = PokemonMaster::Decorator.new(master)
14
43
 
15
- describe '#as_json' do
16
- it 'returns exposed attributes' do
17
- expect(decorator.as_json).to eq(
18
- 'name' => 'John Wick',
19
- 'age' => nil,
20
- 'pokemon' => 'Arcanine'
21
- )
44
+ expect(decorator.as_json).to eq(
45
+ {
46
+ 'age' => 10,
47
+ 'name' => 'Ash Ketchum',
48
+ 'favorite_pokemon' => {
49
+ 'name' => 'pikachu',
50
+ 'nickname' => 'Pikachu'
51
+ },
52
+ 'pokemons' => [{
53
+ 'name' => 'butterfree',
54
+ 'evolution_of' => 'metapod'
55
+ }, {
56
+ 'name' => 'squirtle'
57
+ }, {
58
+ 'name' => 'pikachu'
59
+ }]
60
+ }
61
+ )
62
+ end
22
63
  end
23
64
  end
24
65
  end