Skip to content

Commit 30aa970

Browse files
committed
Fixes issue with class reopening and inheriting.
Exposures and formatters should appear in inherited classes even if added after inheriting.
1 parent 079993f commit 30aa970

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

.rubocop_todo.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
# This configuration was generated by `rubocop --auto-gen-config`
2-
# on 2015-08-02 19:30:25 +0300 using RuboCop version 0.31.0.
2+
# on 2015-08-07 13:25:47 +0300 using RuboCop version 0.31.0.
33
# The point is for the user to remove these configuration records
44
# one by one as the offenses are removed from the code base.
55
# Note that changes in the inspected code, or installation of new
66
# versions of RuboCop, may require this file to be generated again.
77

88
# Offense count: 6
99
Metrics/AbcSize:
10-
Max: 33
10+
Max: 36
1111

1212
# Offense count: 2
1313
# Configuration parameters: CountComments.
1414
Metrics/ClassLength:
15-
Max: 198
15+
Max: 208
1616

1717
# Offense count: 3
1818
Metrics/CyclomaticComplexity:
1919
Max: 11
2020

21-
# Offense count: 208
21+
# Offense count: 209
2222
# Configuration parameters: AllowURI, URISchemes.
2323
Metrics/LineLength:
2424
Max: 146
2525

2626
# Offense count: 8
2727
# Configuration parameters: CountComments.
2828
Metrics/MethodLength:
29-
Max: 28
29+
Max: 31
3030

3131
# Offense count: 5
3232
Metrics/PerceivedComplexity:

lib/grape_entity/entity.rb

+13
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,16 @@ class << self
103103
# Returns all formatters that are registered for this and it's ancestors
104104
# @return [Hash] of formatters
105105
attr_accessor :formatters
106+
attr_accessor :inherited_entities
106107
end
107108

109+
@inherited_entities = []
110+
108111
def self.inherited(subclass)
109112
subclass.root_exposure = root_exposure.try(:dup) || build_root_exposure
110113
subclass.formatters = formatters.try(:dup) || {}
114+
inherited_entities << subclass
115+
subclass.inherited_entities = []
111116
end
112117

113118
# This method is the primary means by which you will declare what attributes
@@ -173,6 +178,10 @@ def self.expose(*args, &block)
173178
@nesting_stack.pop
174179
end
175180
end
181+
182+
inherited_entities.each do |entity|
183+
entity.expose(*args, &block)
184+
end
176185
end
177186

178187
def self.build_root_exposure
@@ -264,6 +273,10 @@ def self.documentation
264273
def self.format_with(name, &block)
265274
fail ArgumentError, 'You must pass a block for formatters' unless block_given?
266275
formatters[name.to_sym] = block
276+
277+
inherited_entities.each do |entity|
278+
entity.format_with(name, &block)
279+
end
267280
end
268281

269282
# This allows you to set a root element name for your representation.

spec/grape_entity/entity_spec.rb

+48
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,47 @@ class Parent < Person
272272
expect(subject.represent({ name: 'bar' }, serializable: true)).to eq(email: nil, name: 'bar')
273273
expect(child_class.represent({ name: 'bar' }, serializable: true)).to eq(email: nil, name: 'foo')
274274
end
275+
276+
describe 'parent class reopening' do
277+
it 'is supported' do
278+
subject.expose :name
279+
child_class = Class.new(subject)
280+
subject.expose :email
281+
282+
object = OpenStruct.new(name: 'bar', email: 'foo@bar')
283+
expected = { name: 'bar', email: 'foo@bar' }
284+
285+
expect(subject.represent(object, serializable: true)).to eq(expected)
286+
expect(child_class.represent(object, serializable: true)).to eq(expected)
287+
end
288+
289+
it 'puts exposures in the right order' do
290+
subject.expose :x
291+
child_class = Class.new(subject) do
292+
expose :z
293+
end
294+
subject.expose :y
295+
object = {
296+
x: 1,
297+
y: 2,
298+
z: 3
299+
}
300+
expect(child_class.represent(object, serializable: true).keys).to eq([:x, :y, :z])
301+
end
302+
303+
it 'just prepends parent class exposures to the inherited class' do
304+
subject.expose :x
305+
child_class = Class.new(subject) do
306+
expose :y, proc: -> (_obj, _opts) { 'specific' }
307+
end
308+
subject.expose :y
309+
object = {
310+
x: 1,
311+
y: 2
312+
}
313+
expect(child_class.represent(object, serializable: true)).to eq(x: 1, y: 'specific')
314+
end
315+
end
275316
end
276317

277318
context 'register formatters' do
@@ -290,6 +331,13 @@ class Parent < Person
290331
expect(child_class.formatters).to eq subject.formatters
291332
end
292333

334+
it 'inherits formatters from ancestors with reopening' do
335+
child_class = Class.new(subject)
336+
subject.format_with :timestamp, &date_formatter
337+
338+
expect(child_class.formatters).to eq subject.formatters
339+
end
340+
293341
it 'does not allow registering a formatter without a block' do
294342
expect { subject.format_with :foo }.to raise_error ArgumentError
295343
end

0 commit comments

Comments
 (0)