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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +3 -3
- data/.rubocop.yml +15 -1
- data/Dockerfile +2 -2
- data/Gemfile +30 -0
- data/Makefile +6 -0
- data/README.md +13 -1
- data/azeroth.gemspec +6 -36
- data/config/check_specs.yml +1 -0
- data/lib/azeroth/controller_interface.rb +3 -2
- data/lib/azeroth/decorator/class_methods.rb +249 -0
- data/lib/azeroth/decorator/hash_builder.rb +1 -0
- data/lib/azeroth/decorator/method_builder.rb +23 -7
- data/lib/azeroth/decorator/options.rb +3 -1
- data/lib/azeroth/decorator.rb +35 -192
- data/lib/azeroth/model.rb +1 -0
- data/lib/azeroth/request_handler/index.rb +7 -33
- data/lib/azeroth/request_handler/pagination.rb +53 -0
- data/lib/azeroth/request_handler.rb +11 -9
- data/lib/azeroth/resourceable/builder.rb +3 -2
- data/lib/azeroth/version.rb +1 -1
- data/spec/dummy/app/models/website/decorator.rb +11 -0
- data/spec/dummy/app/models/website/with_location.rb +21 -0
- data/spec/dummy/app/models/website.rb +4 -0
- data/spec/dummy/config/environments/production.rb +2 -2
- data/spec/dummy/config/puma.rb +3 -3
- data/spec/dummy/db/schema.rb +6 -0
- data/spec/integration/readme/controllers/games_controller_spec.rb +1 -1
- data/spec/integration/yard/azeroth/decorator_spec.rb +15 -0
- data/spec/integration/yard/controllers/games_controller_spec.rb +1 -1
- data/spec/integration/yard/controllers/paginated_documents_controller_spec.rb +1 -1
- data/spec/lib/azeroth/controller_interface_spec.rb +1 -1
- data/spec/lib/azeroth/decorator/method_builder_spec.rb +61 -2
- data/spec/lib/azeroth/decorator_spec.rb +96 -5
- data/spec/lib/azeroth/request_handler/pagination_spec.rb +133 -0
- data/spec/lib/azeroth/request_handler/update_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/support/app/controllers/controller.rb +1 -0
- data/spec/support/matchers/add_method.rb +6 -4
- data/spec/support/shared_examples/request_handler.rb +1 -5
- metadata +17 -569
data/lib/azeroth/decorator.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
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(*
|
100
|
+
def as_json(*)
|
259
101
|
return nil if object.nil?
|
260
102
|
|
261
|
-
return array_as_json(*
|
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, *
|
153
|
+
def method_missing(method_name, *)
|
311
154
|
if object.respond_to?(method_name)
|
312
|
-
object.public_send(method_name, *
|
155
|
+
object.public_send(method_name, *)
|
313
156
|
else
|
314
157
|
super
|
315
158
|
end
|
data/lib/azeroth/model.rb
CHANGED
@@ -8,7 +8,13 @@ module Azeroth
|
|
8
8
|
class Index < RequestHandler
|
9
9
|
private
|
10
10
|
|
11
|
-
delegate :
|
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,
|
12
|
-
autoload :Destroy,
|
13
|
-
autoload :Edit,
|
14
|
-
autoload :Index,
|
15
|
-
autoload :New,
|
16
|
-
autoload :
|
17
|
-
autoload :
|
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, &
|
122
|
-
options.event_registry.trigger(event, controller, &
|
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.
|
125
|
-
klass.
|
125
|
+
klass.helper_method(model.name)
|
126
|
+
klass.helper_method(model.plural)
|
126
127
|
end
|
127
128
|
end
|
128
129
|
end
|
data/lib/azeroth/version.rb
CHANGED
@@ -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
|
@@ -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 =
|
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(
|
101
|
+
logger = ActiveSupport::Logger.new($stdout)
|
102
102
|
logger.formatter = config.log_formatter
|
103
103
|
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
104
104
|
end
|
data/spec/dummy/config/puma.rb
CHANGED
@@ -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'
|
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'
|
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'
|
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
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -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
|
@@ -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
|