azeroth 0.6.3 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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