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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff267cb06b99dea008313ebf30569e33565cbbe210602b3008bd859016c28755
4
- data.tar.gz: 494a34050515c0dcd7e05ecdce0151e26f089e0ae39d3c1652a38d210ec1f99d
3
+ metadata.gz: dbddea8e78679a5a20ccb7c04fd94da0f55718713add9d352d97bff07921df9c
4
+ data.tar.gz: e5cc1161a4b36df822458101ce8e4f443a8be05eaf5a0430a0ae09db313e8794
5
5
  SHA512:
6
- metadata.gz: 1e89465dc060b9ab4a0b9a6c95e6b2cb054ab08efd32ebf312bbe94f751f5d505f20230632e8435d0de57898f9bbd9875215fd23c59f51a1de6516a2bfb0e273
7
- data.tar.gz: fe6c480bc37d5e03ea61eb22df4c66154537fca6917d671e19fac1ce7bdf5de5051dfd736de0c7529ffa240512f615bd5a1c1a2e5ed285a3326956bd59758dd4
6
+ metadata.gz: dd4a272074e69969eddf5d7c3fa3b96d1b0154e117d81b6be419b59573169e100773ecbd97d96f2465a72a5c5e0458c3d7e13d12b5e2c1ed710412a8139e7b4c
7
+ data.tar.gz: 0eb07f9c6da10355a9c9db82e555d0eecfaefa75abce57603109efa8aa841a53a70f372afeef6dafe5df9e88504a4180e76f66a8060cbf5841dbdeb6895f6a9f
@@ -1,8 +1,24 @@
1
1
  version: 2
2
+ workflows:
3
+ version: 2
4
+ test-and-build:
5
+ jobs:
6
+ - test:
7
+ filters:
8
+ tags:
9
+ only: /.*/
10
+ - build-and-release:
11
+ requires: [test]
12
+ filters:
13
+ tags:
14
+ only: /\d+\.\d+\.\d+/
15
+ branches:
16
+ only:
17
+ - master
2
18
  jobs:
3
- build:
19
+ test:
4
20
  docker:
5
- - image: darthjee/circleci_rails_gems:0.5.1
21
+ - image: darthjee/circleci_rails_gems:0.5.4
6
22
  environment:
7
23
  PROJECT: azeroth
8
24
  steps:
@@ -34,3 +50,22 @@ jobs:
34
50
  - run:
35
51
  name: Check unit tests
36
52
  command: check_specs
53
+ build-and-release:
54
+ docker:
55
+ - image: darthjee/circleci_rails_gems:0.5.4
56
+ environment:
57
+ PROJECT: azeroth
58
+ steps:
59
+ - checkout
60
+ - run:
61
+ name: Bundle Install
62
+ command: bundle install
63
+ - run:
64
+ name: Signin
65
+ command: build_gem.sh signin
66
+ - run:
67
+ name: Build Gem
68
+ command: build_gem.sh build
69
+ - run:
70
+ name: Push Gem
71
+ command: build_gem.sh push
@@ -7,6 +7,7 @@ AllCops:
7
7
  Metrics/BlockLength:
8
8
  Exclude:
9
9
  - 'spec/**/*_spec.rb'
10
+ - 'spec/dummy/db/schema.rb'
10
11
  - 'spec/support/shared_*/**/*.rb'
11
12
  - 'azeroth.gemspec'
12
13
 
@@ -24,3 +25,13 @@ Style/HashTransformKeys:
24
25
 
25
26
  Style/HashTransformValues:
26
27
  Enabled: true
28
+
29
+ RSpec/ExampleLength:
30
+ Exclude:
31
+ - 'spec/integration/readme/**/*_spec.rb'
32
+ - 'spec/integration/yard/**/*_spec.rb'
33
+
34
+ RSpec/MultipleExpectations:
35
+ Exclude:
36
+ - 'spec/integration/readme/**/*_spec.rb'
37
+ - 'spec/integration/yard/**/*_spec.rb'
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
- FROM darthjee/rails_gems:0.5.2 as base
2
- FROM darthjee/scripts:0.1.7 as scripts
1
+ FROM darthjee/rails_gems:0.5.4 as base
2
+ FROM darthjee/scripts:0.1.8 as scripts
3
3
 
4
4
  ######################################
5
5
 
data/README.md CHANGED
@@ -11,4 +11,167 @@ Azeroth
11
11
 
12
12
  Yard Documentation
13
13
  -------------------
14
- [https://www.rubydoc.info/gems/azeroth/0.6.3](https://www.rubydoc.info/gems/azeroth/0.6.3)
14
+ [https://www.rubydoc.info/gems/azeroth/0.7.1](https://www.rubydoc.info/gems/azeroth/0.7.2)
15
+
16
+ Azeroth has been designed making the coding of controllers easier
17
+ as routes in controllers are usually copy, paste and replace of same
18
+ code.
19
+
20
+ Azeroth was originally developed for controller actions
21
+ which will respond with json or template rendering based
22
+ on the requested format `.json` or `.html` where `html` rendering
23
+ does not perform database operations
24
+
25
+ Future versions will enable `html` rendering to also perform
26
+ database operations.
27
+
28
+ Installation
29
+ ---------------
30
+
31
+ - Install it
32
+
33
+ ```ruby
34
+ gem install azeroth
35
+ ```
36
+
37
+ - Or add Sinclair to your `Gemfile` and `bundle install`:
38
+
39
+ ```ruby
40
+ gem 'azeroth'
41
+ ```
42
+
43
+ ```bash
44
+ bundle install azeroth
45
+ ```
46
+
47
+ Usage
48
+ -----
49
+
50
+ ## Azeroth::Resourceable
51
+
52
+ [Resourceable](https://www.rubydoc.info/gems/azeroth/Azeroth/Resourceable)
53
+ module adds class method [resource_for](https://www.rubydoc.info/gems/azeroth/Azeroth/Resourceable/ClassMethods#resource_for-instance_method)
54
+ which adds a resource and action methods for `create`, `show`, `index`,
55
+ `update`, `delete`, `edit`
56
+
57
+ It accepts options
58
+ - only: List of actions to be built
59
+ - except: List of actions to not to be built
60
+ - decorator: Decorator class or flag allowing/disallowing decorators
61
+ - before_save: Method/Proc to be ran before saving the resource on create or update
62
+ - build_with: Method/Block to be ran when building the reource on create
63
+
64
+ ```ruby
65
+ # publishers_controller.rb
66
+
67
+ class PublishersController < ApplicationController
68
+ include Azeroth::Resourceable
69
+ skip_before_action :verify_authenticity_token
70
+
71
+ resource_for :publisher, only: %i[create index]
72
+ end
73
+ ```
74
+
75
+ ```ruby
76
+ # games_controller.rb
77
+
78
+ class GamesController < ApplicationController
79
+ include Azeroth::Resourceable
80
+ skip_before_action :verify_authenticity_token
81
+
82
+ resource_for :game, except: :delete
83
+
84
+ private
85
+
86
+ def games
87
+ publisher.games
88
+ end
89
+
90
+ def publisher
91
+ @publisher ||= Publisher.find(publisher_id)
92
+ end
93
+
94
+ def publisher_id
95
+ params.require(:publisher_id)
96
+ end
97
+ end
98
+ ```
99
+
100
+ ```ruby
101
+ # pokemons_controller.rb
102
+
103
+ class PokemonsController < ApplicationController
104
+ include Azeroth::Resourceable
105
+
106
+ resource_for :pokemon,
107
+ only: %i[create update],
108
+ before_save: :set_favorite
109
+
110
+ private
111
+
112
+ def set_favorite
113
+ pokemon.favorite = true
114
+ end
115
+
116
+ def pokemons
117
+ master.pokemons
118
+ end
119
+
120
+ def master
121
+ @master ||= PokemonMaster.find(master_id)
122
+ end
123
+
124
+ def master_id
125
+ params.require(:pokemon_master_id)
126
+ end
127
+ end
128
+ ```
129
+
130
+ ## Azeroth::Decorator
131
+
132
+ [Decorators](https://www.rubydoc.info/gems/azeroth/Azeroth/Decorator) are
133
+ used to define how an object is exposed as json on controller responses
134
+ defining which and how fields will be exposed
135
+
136
+ ```ruby
137
+ # pokemon/decorator.rb
138
+
139
+ class Pokemon::Decorator < Azeroth::Decorator
140
+ expose :name
141
+ expose :previous_form_name, as: :evolution_of, if: :evolution?
142
+
143
+ def evolution?
144
+ previous_form
145
+ end
146
+
147
+ def previous_form_name
148
+ previous_form.name
149
+ end
150
+ end
151
+ ```
152
+
153
+ ```ruby
154
+ # pokemon/favorite_decorator.rb
155
+
156
+ class Pokemon::FavoriteDecorator < Pokemon::Decorator
157
+ expose :nickname
158
+ end
159
+ ```
160
+
161
+ ```ruby
162
+ # pokemon_master/decorator.rb
163
+
164
+ class PokemonMaster < ActiveRecord::Base
165
+ has_one :favorite_pokemon, -> { where(favorite: true) },
166
+ class_name: 'Pokemon'
167
+ has_many :pokemons
168
+ end
169
+ ```
170
+
171
+ Exposing is done through the class method
172
+ [expose](https://www.rubydoc.info/gems/azeroth/Azeroth/Decorator#expose-class_method)
173
+ which accepts several options:
174
+
175
+ - as: custom key to expose
176
+ - if: method/block to be called checking if an attribute should or should not be exposed
177
+ - decorator: flag to use or not a decorator or decorator class to be used
@@ -18,18 +18,19 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ['lib']
20
20
 
21
- gem.add_runtime_dependency 'activesupport', '~> 5.2.0'
21
+ gem.add_runtime_dependency 'activesupport', '~> 5.2.4.3'
22
22
  gem.add_runtime_dependency 'darthjee-active_ext', '>= 1.3.2'
23
- gem.add_runtime_dependency 'sinclair', '>= 1.6.4'
23
+ gem.add_runtime_dependency 'jace', '>= 0.0.3'
24
+ gem.add_runtime_dependency 'sinclair', '>= 1.6.5'
24
25
 
25
- gem.add_development_dependency 'actionpack', '5.2.4.2'
26
- gem.add_development_dependency 'activerecord', '5.2.4.2'
26
+ gem.add_development_dependency 'actionpack', '5.2.4.3'
27
+ gem.add_development_dependency 'activerecord', '5.2.4.3'
27
28
  gem.add_development_dependency 'bundler', '1.16.1'
28
29
  gem.add_development_dependency 'factory_bot', '5.2.0'
29
30
  gem.add_development_dependency 'nokogiri', '1.10.9'
30
31
  gem.add_development_dependency 'pry', '0.12.2'
31
32
  gem.add_development_dependency 'pry-nav', '0.3.0'
32
- gem.add_development_dependency 'rails', '5.2.4.2'
33
+ gem.add_development_dependency 'rails', '5.2.4.3'
33
34
  gem.add_development_dependency 'rails-controller-testing', '1.0.4'
34
35
  gem.add_development_dependency 'rake', '13.0.1'
35
36
  gem.add_development_dependency 'reek', '5.6.0'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'sinclair'
4
+ require 'jace'
4
5
 
5
6
  # @api public
6
7
  #
@@ -84,6 +84,8 @@ module Azeroth
84
84
  # to be called
85
85
  # checking if an attribute should or should not
86
86
  # be exposed
87
+ # @option options_hash decorator [FalseClass,TrueClass,Class]
88
+ # flag to use or not a decorator or decorator class to be used
87
89
  #
88
90
  # @return [Array<Symbol>]
89
91
  #
@@ -104,6 +106,125 @@ module Azeroth
104
106
  # end
105
107
  # end
106
108
  # end
109
+ #
110
+ # @example With relations
111
+ # # pokemon/decorator.rb
112
+ #
113
+ # class Pokemon::Decorator < Azeroth::Decorator
114
+ # expose :name
115
+ # expose :previous_form_name, as: :evolution_of, if: :evolution?
116
+ #
117
+ # def evolution?
118
+ # previous_form
119
+ # end
120
+ #
121
+ # def previous_form_name
122
+ # previous_form.name
123
+ # end
124
+ # end
125
+ #
126
+ # # pokemon/favorite_decorator.rb
127
+ #
128
+ # class Pokemon::FavoriteDecorator < Pokemon::Decorator
129
+ # expose :nickname
130
+ # end
131
+ #
132
+ # # pokemon_master/decorator.rb
133
+ #
134
+ # class PokemonMaster < ActiveRecord::Base
135
+ # has_one :favorite_pokemon, -> { where(favorite: true) },
136
+ # class_name: 'Pokemon'
137
+ # has_many :pokemons
138
+ # end
139
+ #
140
+ # # pokemon.rb
141
+ #
142
+ # class Pokemon < ActiveRecord::Base
143
+ # belongs_to :pokemon_master
144
+ # has_one :previous_form,
145
+ # class_name: 'Pokemon',
146
+ # foreign_key: :previous_form_id
147
+ # end
148
+ #
149
+ # # pokemon_master.rb
150
+ #
151
+ # class PokemonMaster::Decorator < Azeroth::Decorator
152
+ # expose :name
153
+ # expose :age
154
+ # expose :favorite_pokemon, decorator: Pokemon::FavoriteDecorator
155
+ # expose :pokemons
156
+ #
157
+ # def name
158
+ # [
159
+ # first_name,
160
+ # last_name
161
+ # ].compact.join(' ')
162
+ # end
163
+ # end
164
+ #
165
+ # # schema.rb
166
+ #
167
+ # ActiveRecord::Schema.define do
168
+ # self.verbose = false
169
+ #
170
+ # create_table :pokemon_masters, force: true do |t|
171
+ # t.string :first_name, null: false
172
+ # t.string :last_name
173
+ # t.integer :age, null: false
174
+ # end
175
+ #
176
+ # create_table :pokemons, force: true do |t|
177
+ # t.string :name, null: false
178
+ # t.string :nickname
179
+ # t.integer :pokemon_master_id
180
+ # t.boolean :favorite
181
+ # t.integer :previous_form_id
182
+ # t.index %i[pokemon_master_id favorite], unique: true
183
+ # end
184
+ #
185
+ # add_foreign_key 'pokemons', 'pokemon_masters'
186
+ # end
187
+ #
188
+ # # test.rb
189
+ #
190
+ # master = PokemonMaster.create(
191
+ # first_name: 'Ash',
192
+ # last_name: 'Ketchum',
193
+ # age: 10
194
+ # )
195
+ #
196
+ # master.create_favorite_pokemon(
197
+ # name: 'pikachu',
198
+ # nickname: 'Pikachu'
199
+ # )
200
+ #
201
+ # metapod = Pokemon.create(name: :metapod)
202
+ #
203
+ # master.pokemons.create(
204
+ # name: 'butterfree', previous_form: metapod
205
+ # )
206
+ # master.pokemons.create(name: 'squirtle')
207
+ #
208
+ # decorator = PokemonMaster::Decorator.new(master)
209
+ #
210
+ # decorator.as_json
211
+ # # returns
212
+ # # {
213
+ # # 'age' => 10,
214
+ # # 'name' => 'Ash Ketchum',
215
+ # # 'favorite_pokemon' => {
216
+ # # 'name' => 'pikachu',
217
+ # # 'nickname' => 'Pikachu'
218
+ # # },
219
+ # # 'pokemons' => [{
220
+ # # 'name' => 'butterfree',
221
+ # # 'evolution_of' => 'metapod'
222
+ # # }, {
223
+ # # 'name' => 'squirtle'
224
+ # # }, {
225
+ # # 'name' => 'pikachu'
226
+ # # }]
227
+ # # }
107
228
  def expose(attribute, **options_hash)
108
229
  options = Decorator::Options.new(options_hash)
109
230
 
@@ -136,6 +257,8 @@ module Azeroth
136
257
  #
137
258
  # @return [Hash]
138
259
  def as_json(*args)
260
+ return nil if object.nil?
261
+
139
262
  return array_as_json(*args) if enum?
140
263
 
141
264
  HashBuilder.new(self).as_json
@@ -97,9 +97,9 @@ module Azeroth
97
97
  #
98
98
  # @return [Class<Decorator>, NilClass]
99
99
  def decorator_from_options
100
- return options.decorator if options.decorator
100
+ return options.decorator if options.decorator_defined?
101
101
 
102
- Azeroth::DummyDecorator unless options.any_decorator?
102
+ Azeroth::DummyDecorator unless options.decorator
103
103
  end
104
104
 
105
105
  # @private