azeroth 1.0.0 → 2.0.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +3 -3
  3. data/.rubocop.yml +15 -1
  4. data/Dockerfile +2 -2
  5. data/Gemfile +30 -0
  6. data/Makefile +6 -0
  7. data/README.md +13 -1
  8. data/azeroth.gemspec +6 -36
  9. data/config/check_specs.yml +1 -0
  10. data/lib/azeroth/controller_interface.rb +3 -2
  11. data/lib/azeroth/decorator/class_methods.rb +249 -0
  12. data/lib/azeroth/decorator/hash_builder.rb +1 -0
  13. data/lib/azeroth/decorator/method_builder.rb +23 -7
  14. data/lib/azeroth/decorator/options.rb +3 -1
  15. data/lib/azeroth/decorator.rb +35 -192
  16. data/lib/azeroth/model.rb +1 -0
  17. data/lib/azeroth/request_handler/index.rb +7 -33
  18. data/lib/azeroth/request_handler/pagination.rb +53 -0
  19. data/lib/azeroth/request_handler.rb +11 -9
  20. data/lib/azeroth/resourceable/builder.rb +3 -2
  21. data/lib/azeroth/version.rb +1 -1
  22. data/spec/dummy/app/models/website/decorator.rb +11 -0
  23. data/spec/dummy/app/models/website/with_location.rb +21 -0
  24. data/spec/dummy/app/models/website.rb +4 -0
  25. data/spec/dummy/config/environments/production.rb +2 -2
  26. data/spec/dummy/config/puma.rb +3 -3
  27. data/spec/dummy/db/schema.rb +6 -0
  28. data/spec/integration/readme/controllers/games_controller_spec.rb +1 -1
  29. data/spec/integration/yard/azeroth/decorator_spec.rb +15 -0
  30. data/spec/integration/yard/controllers/games_controller_spec.rb +1 -1
  31. data/spec/integration/yard/controllers/paginated_documents_controller_spec.rb +1 -1
  32. data/spec/lib/azeroth/controller_interface_spec.rb +1 -1
  33. data/spec/lib/azeroth/decorator/method_builder_spec.rb +61 -2
  34. data/spec/lib/azeroth/decorator_spec.rb +96 -5
  35. data/spec/lib/azeroth/request_handler/pagination_spec.rb +133 -0
  36. data/spec/lib/azeroth/request_handler/update_spec.rb +1 -1
  37. data/spec/spec_helper.rb +1 -1
  38. data/spec/support/app/controllers/controller.rb +1 -0
  39. data/spec/support/matchers/add_method.rb +6 -4
  40. data/spec/support/shared_examples/request_handler.rb +1 -5
  41. metadata +17 -569
@@ -40,200 +40,42 @@ module Azeroth
40
40
  # # 'pokemon' => 'Arcanine'
41
41
  # # }
42
42
  class Decorator
43
+ autoload :ClassMethods, 'azeroth/decorator/class_methods'
43
44
  autoload :HashBuilder, 'azeroth/decorator/hash_builder'
44
45
  autoload :KeyValueExtractor, 'azeroth/decorator/key_value_extractor'
45
46
  autoload :MethodBuilder, 'azeroth/decorator/method_builder'
46
47
  autoload :Options, 'azeroth/decorator/options'
47
48
 
48
- class << self
49
- # @api private
50
- #
51
- # All attributes exposed
52
- #
53
- # @return [Hash<Symbol,Hash>]
54
- def attributes_map
55
- @attributes_map ||= build_attributes_map
56
- end
57
-
58
- private
59
-
60
- # @api private
61
- # @private
62
- #
63
- # Initialize attributes to be exposed map
64
- #
65
- # When the class inherity from another
66
- # decorator, the new class should expose the
67
- # same attributes.
68
- #
69
- # @return [Hash<Symbol,Hash>]
70
- def build_attributes_map
71
- superclass.try(:attributes_map).dup || {}
72
- end
73
-
74
- # @visibility public
75
- # @api public
76
- # @private
77
- #
78
- # Expose attributes on json decorated
79
- #
80
- # @param attribute [Symbol,String] attribute to be exposed
81
- # @param options_hash [Hash] exposing options
82
- # @option options_hash as [Symbol,String] custom key
83
- # to expose
84
- # @option options_hash if [Symbol,Proc] method/block
85
- # to be called
86
- # checking if an attribute should or should not
87
- # be exposed
88
- # @option options_hash decorator [FalseClass,TrueClass,Class]
89
- # flag to use or not a decorator or decorator class to be used
90
- #
91
- # @return [Array<Symbol>]
92
- #
93
- # @example
94
- # class DummyModel
95
- # include ActiveModel::Model
96
- #
97
- # attr_accessor :id, :first_name, :last_name, :age,
98
- # :favorite_pokemon
99
- #
100
- # class Decorator < Azeroth::Decorator
101
- # expose :name
102
- # expose :age
103
- # expose :favorite_pokemon, as: :pokemon
104
- #
105
- # def name
106
- # [object.first_name, object.last_name].join(' ')
107
- # end
108
- # end
109
- # end
110
- #
111
- # @example With relations
112
- # # pokemon/decorator.rb
113
- #
114
- # class Pokemon::Decorator < Azeroth::Decorator
115
- # expose :name
116
- # expose :previous_form_name, as: :evolution_of, if: :evolution?
117
- #
118
- # def evolution?
119
- # previous_form
120
- # end
121
- #
122
- # def previous_form_name
123
- # previous_form.name
124
- # end
125
- # end
126
- #
127
- # # pokemon/favorite_decorator.rb
128
- #
129
- # class Pokemon::FavoriteDecorator < Pokemon::Decorator
130
- # expose :nickname
131
- # end
132
- #
133
- # # pokemon_master/decorator.rb
134
- #
135
- # class PokemonMaster < ActiveRecord::Base
136
- # has_one :favorite_pokemon, -> { where(favorite: true) },
137
- # class_name: 'Pokemon'
138
- # has_many :pokemons
139
- # end
140
- #
141
- # # pokemon.rb
142
- #
143
- # class Pokemon < ActiveRecord::Base
144
- # belongs_to :pokemon_master
145
- # has_one :previous_form,
146
- # class_name: 'Pokemon',
147
- # foreign_key: :previous_form_id
148
- # end
149
- #
150
- # # pokemon_master.rb
151
- #
152
- # class PokemonMaster::Decorator < Azeroth::Decorator
153
- # expose :name
154
- # expose :age
155
- # expose :favorite_pokemon, decorator: Pokemon::FavoriteDecorator
156
- # expose :pokemons
157
- #
158
- # def name
159
- # [
160
- # first_name,
161
- # last_name
162
- # ].compact.join(' ')
163
- # end
164
- # end
165
- #
166
- # # schema.rb
167
- #
168
- # ActiveRecord::Schema.define do
169
- # self.verbose = false
170
- #
171
- # create_table :pokemon_masters, force: true do |t|
172
- # t.string :first_name, null: false
173
- # t.string :last_name
174
- # t.integer :age, null: false
175
- # end
176
- #
177
- # create_table :pokemons, force: true do |t|
178
- # t.string :name, null: false
179
- # t.string :nickname
180
- # t.integer :pokemon_master_id
181
- # t.boolean :favorite
182
- # t.integer :previous_form_id
183
- # t.index %i[pokemon_master_id favorite], unique: true
184
- # end
185
- #
186
- # add_foreign_key 'pokemons', 'pokemon_masters'
187
- # end
188
- #
189
- # # test.rb
190
- #
191
- # master = PokemonMaster.create(
192
- # first_name: 'Ash',
193
- # last_name: 'Ketchum',
194
- # age: 10
195
- # )
196
- #
197
- # master.create_favorite_pokemon(
198
- # name: 'pikachu',
199
- # nickname: 'Pikachu'
200
- # )
201
- #
202
- # metapod = Pokemon.create(name: :metapod)
203
- #
204
- # master.pokemons.create(
205
- # name: 'butterfree', previous_form: metapod
206
- # )
207
- # master.pokemons.create(name: 'squirtle')
208
- #
209
- # decorator = PokemonMaster::Decorator.new(master)
210
- #
211
- # decorator.as_json
212
- # # returns
213
- # # {
214
- # # 'age' => 10,
215
- # # 'name' => 'Ash Ketchum',
216
- # # 'favorite_pokemon' => {
217
- # # 'name' => 'pikachu',
218
- # # 'nickname' => 'Pikachu'
219
- # # },
220
- # # 'pokemons' => [{
221
- # # 'name' => 'butterfree',
222
- # # 'evolution_of' => 'metapod'
223
- # # }, {
224
- # # 'name' => 'squirtle'
225
- # # }, {
226
- # # 'name' => 'pikachu'
227
- # # }]
228
- # # }
229
- def expose(attribute, **options_hash)
230
- options = Decorator::Options.new(options_hash)
231
-
232
- MethodBuilder.build_reader(self, attribute)
49
+ extend ClassMethods
233
50
 
234
- attributes_map[attribute] = options
235
- end
236
- end
51
+ # @method self.expose(attribute, **options_hash)
52
+ # @visibility public
53
+ # @api public
54
+ # @private
55
+ #
56
+ # Expose attributes on json decorated
57
+ #
58
+ # @param attribute [Symbol,String] attribute to be exposed
59
+ # @param options_hash [Hash] exposing options
60
+ # @option options_hash as [Symbol,String] custom key
61
+ # to expose the value as
62
+ # @option options_hash if [Symbol,Proc] method/block
63
+ # to be called
64
+ # checking if an attribute should or should not
65
+ # be exposed
66
+ # @option options_hash decorator [FalseClass,TrueClass,Class]
67
+ # flag to use or not a decorator or decorator class to be used
68
+ # @option options_hash reader [Boolean] Flag indicating if a reader
69
+ # to access the attribute should be created. usefull if you want
70
+ # method_missing to take over
71
+ # @option options_hash override [Boolean] Flag indicating if an
72
+ # existing method should be overriden.
73
+ # This is useful when a method acessor was included from another module
74
+ #
75
+ # @return [Array<Symbol>]
76
+ #
77
+ # @see Decorator::ClassMethods#expose Decorator::ClassMethods#expose
78
+ # for examples
237
79
 
238
80
  # @api private
239
81
  #
@@ -255,10 +97,10 @@ module Azeroth
255
97
  # @param args [Hash] options (to be implemented)
256
98
  #
257
99
  # @return [Hash]
258
- def as_json(*args)
100
+ def as_json(*)
259
101
  return nil if object.nil?
260
102
 
261
- return array_as_json(*args) if enum?
103
+ return array_as_json(*) if enum?
262
104
 
263
105
  HashBuilder.new(self).as_json
264
106
  end
@@ -266,6 +108,7 @@ module Azeroth
266
108
  private
267
109
 
268
110
  attr_reader :object
111
+
269
112
  # @method object
270
113
  # @api private
271
114
  #
@@ -307,9 +150,9 @@ module Azeroth
307
150
  # method called
308
151
  #
309
152
  # @return [Object]
310
- def method_missing(method_name, *args)
153
+ def method_missing(method_name, *)
311
154
  if object.respond_to?(method_name)
312
- object.public_send(method_name, *args)
155
+ object.public_send(method_name, *)
313
156
  else
314
157
  super
315
158
  end
data/lib/azeroth/model.rb CHANGED
@@ -54,6 +54,7 @@ module Azeroth
54
54
  private
55
55
 
56
56
  attr_reader :options
57
+
57
58
  # @method options
58
59
  # @api private
59
60
  # @private
@@ -8,7 +8,13 @@ module Azeroth
8
8
  class Index < RequestHandler
9
9
  private
10
10
 
11
- delegate :paginated?, :per_page, to: :options
11
+ delegate :per_page, :offset, :limit,
12
+ :current_page, to: :pagination
13
+ delegate :paginated?, to: :options
14
+
15
+ def pagination
16
+ @pagination ||= Pagination.new(params, options)
17
+ end
12
18
 
13
19
  # @private
14
20
  #
@@ -45,29 +51,6 @@ module Azeroth
45
51
  scoped_entries.offset(offset).limit(limit)
46
52
  end
47
53
 
48
- # @private
49
- #
50
- # offest used in pagination
51
- #
52
- # offset is retrieved from +params[:per_page]+
53
- # or +options.per_page+
54
- #
55
- # @return [Integer]
56
- def offset
57
- (current_page - 1) * limit
58
- end
59
-
60
- # @private
61
- #
62
- # limit used in pagination
63
- #
64
- # limit is retrieved from +params[:page]+
65
- #
66
- # @return [Integer]
67
- def limit
68
- (params[:per_page] || per_page).to_i
69
- end
70
-
71
54
  # @private
72
55
  #
73
56
  # default scope of elements
@@ -77,15 +60,6 @@ module Azeroth
77
60
  controller.send(model.plural)
78
61
  end
79
62
 
80
- # @private
81
- #
82
- # calculates current page
83
- #
84
- # @return [Integer]
85
- def current_page
86
- (params[:page] || 1).to_i
87
- end
88
-
89
63
  # @private
90
64
  #
91
65
  # calculates how many pages are there
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Azeroth
4
+ class RequestHandler
5
+ # @api private
6
+ #
7
+ # Wrapper for request finding pagination attributes
8
+ class Pagination
9
+ def initialize(params, options)
10
+ @params = params
11
+ @options = options
12
+ end
13
+
14
+ delegate :per_page, to: :options
15
+
16
+ # @private
17
+ #
18
+ # offest used in pagination
19
+ #
20
+ # offset is retrieved from +params[:per_page]+
21
+ # or +options.per_page+
22
+ #
23
+ # @return [Integer]
24
+ def offset
25
+ (current_page - 1) * limit
26
+ end
27
+
28
+ # @private
29
+ #
30
+ # limit used in pagination
31
+ #
32
+ # limit is retrieved from +params[:page]+
33
+ #
34
+ # @return [Integer]
35
+ def limit
36
+ (params[:per_page] || per_page).to_i
37
+ end
38
+
39
+ # @private
40
+ #
41
+ # calculates current page
42
+ #
43
+ # @return [Integer]
44
+ def current_page
45
+ (params[:page] || 1).to_i
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :params, :options
51
+ end
52
+ end
53
+ end
@@ -8,13 +8,14 @@ module Azeroth
8
8
  # Request handler sends messages to the controller
9
9
  # in order to get the resource and rendering the response
10
10
  class RequestHandler
11
- autoload :Create, 'azeroth/request_handler/create'
12
- autoload :Destroy, 'azeroth/request_handler/destroy'
13
- autoload :Edit, 'azeroth/request_handler/edit'
14
- autoload :Index, 'azeroth/request_handler/index'
15
- autoload :New, 'azeroth/request_handler/new'
16
- autoload :Show, 'azeroth/request_handler/show'
17
- autoload :Update, 'azeroth/request_handler/update'
11
+ autoload :Create, 'azeroth/request_handler/create'
12
+ autoload :Destroy, 'azeroth/request_handler/destroy'
13
+ autoload :Edit, 'azeroth/request_handler/edit'
14
+ autoload :Index, 'azeroth/request_handler/index'
15
+ autoload :New, 'azeroth/request_handler/new'
16
+ autoload :Pagination, 'azeroth/request_handler/pagination'
17
+ autoload :Show, 'azeroth/request_handler/show'
18
+ autoload :Update, 'azeroth/request_handler/update'
18
19
 
19
20
  # @param controller [ApplicationController]
20
21
  # @param model [Azeroth::Model]
@@ -45,6 +46,7 @@ module Azeroth
45
46
  private
46
47
 
47
48
  attr_reader :controller, :model, :options
49
+
48
50
  # @method controller
49
51
  # @api private
50
52
  # @private
@@ -118,8 +120,8 @@ module Azeroth
118
120
  # Run a block triggering the event
119
121
  #
120
122
  # @return [Object] Result of given block
121
- def trigger_event(event, &block)
122
- options.event_registry.trigger(event, controller, &block)
123
+ def trigger_event(event, &)
124
+ options.event_registry.trigger(event, controller, &)
123
125
  end
124
126
 
125
127
  # @private
@@ -31,6 +31,7 @@ module Azeroth
31
31
  private
32
32
 
33
33
  attr_reader :klass, :model, :options
34
+
34
35
  # @method klass
35
36
  # @api private
36
37
  # @private
@@ -121,8 +122,8 @@ module Azeroth
121
122
  #
122
123
  # @return [String]
123
124
  def add_helpers
124
- klass.public_send(:helper_method, model.name)
125
- klass.public_send(:helper_method, model.plural)
125
+ klass.helper_method(model.name)
126
+ klass.helper_method(model.plural)
126
127
  end
127
128
  end
128
129
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Azeroth
4
- VERSION = '1.0.0'
4
+ VERSION = '2.0.0'
5
5
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Website < ActiveRecord::Base
4
+ class Decorator < Azeroth::Decorator
5
+ include WithLocation
6
+
7
+ expose :location, override: false
8
+
9
+ alias website object
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Website < ActiveRecord::Base
4
+ module WithLocation
5
+ def location
6
+ "#{protocol}://#{domain}:#{port}"
7
+ end
8
+
9
+ def protocol
10
+ website.protocol || '*'
11
+ end
12
+
13
+ def domain
14
+ website.domain || '*'
15
+ end
16
+
17
+ def port
18
+ website.port || '*'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Website < ActiveRecord::Base
4
+ end
@@ -89,7 +89,7 @@ Rails.application.configure do
89
89
  config.active_support.deprecation = :notify
90
90
 
91
91
  # Use default logging formatter so that PID and timestamp are not suppressed.
92
- config.log_formatter = ::Logger::Formatter.new
92
+ config.log_formatter = Logger::Formatter.new
93
93
 
94
94
  # Use a different logger for distributed setups.
95
95
  # require 'syslog/logger'
@@ -98,7 +98,7 @@ Rails.application.configure do
98
98
  # )
99
99
 
100
100
  if ENV['RAILS_LOG_TO_STDOUT'].present?
101
- logger = ActiveSupport::Logger.new(STDOUT)
101
+ logger = ActiveSupport::Logger.new($stdout)
102
102
  logger.formatter = config.log_formatter
103
103
  config.logger = ActiveSupport::TaggedLogging.new(logger)
104
104
  end
@@ -6,17 +6,17 @@
6
6
  # the maximum value specified for Puma. Default is set to 5 threads for minimum
7
7
  # and maximum; this matches the default thread size of Active Record.
8
8
  #
9
- threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }
9
+ threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
10
10
  threads threads_count, threads_count
11
11
 
12
12
  # Specifies the `port` that Puma will listen on to receive requests;
13
13
  # default is 3000.
14
14
  #
15
- port ENV.fetch('PORT') { 3000 }
15
+ port ENV.fetch('PORT', 3000)
16
16
 
17
17
  # Specifies the `environment` that Puma will run in.
18
18
  #
19
- environment ENV.fetch('RAILS_ENV') { 'development' }
19
+ environment ENV.fetch('RAILS_ENV', 'development')
20
20
 
21
21
  # Specifies the number of `workers` to boot in clustered mode.
22
22
  # Workers are forked webserver processes. If using threads and workers together
@@ -51,4 +51,10 @@ ActiveRecord::Schema.define do
51
51
  t.string :name, null: false
52
52
  t.string :director, null: false
53
53
  end
54
+
55
+ create_table :websites, force: true do |t|
56
+ t.string :domain, null: false
57
+ t.integer :port, limit: 2
58
+ t.string :protocol, limit: 5
59
+ end
54
60
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe GamesController, controller: true do
5
+ describe GamesController, :controller do
6
6
  describe 'readme' do
7
7
  describe 'POST create' do
8
8
  it 'create game' do
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spec_helper'
4
+
3
5
  describe Azeroth::Decorator do
4
6
  describe 'yard' do
5
7
  describe '#as_json' do
@@ -60,6 +62,19 @@ describe Azeroth::Decorator do
60
62
  }
61
63
  )
62
64
  end
65
+
66
+ it 'example without override' do
67
+ website = Website.create(
68
+ protocol: :http,
69
+ domain: 'google.com'
70
+ )
71
+
72
+ decorator = Website::Decorator.new(website)
73
+
74
+ expect(decorator.as_json).to eq(
75
+ 'location' => 'http://google.com:*'
76
+ )
77
+ end
63
78
  end
64
79
  end
65
80
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe GamesController, controller: true do
5
+ describe GamesController, :controller do
6
6
  describe 'yard' do
7
7
  describe 'POST create' do
8
8
  it 'create game' do
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe PaginatedDocumentsController, controller: true do
5
+ describe PaginatedDocumentsController, :controller do
6
6
  describe 'yard' do
7
7
  describe 'GET index' do
8
8
  before { create_list(:document, 30) }
@@ -11,7 +11,7 @@ describe Azeroth::ControllerInterface do
11
11
  describe '#add_headers' do
12
12
  let(:controller_headers) do
13
13
  {
14
- 'old_key': 100
14
+ old_key: 100
15
15
  }
16
16
  end
17
17