Skip to content

Commit 11914e8

Browse files
committed
Merge pull request #134 from etehtsea/refactoring
Some subtle improvements
2 parents d207258 + a55cf24 commit 11914e8

File tree

3 files changed

+28
-82
lines changed

3 files changed

+28
-82
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* [#114](https://github.com/intridea/grape-entity/pull/114): Added 'only' option that selects which attributes should be returned - [@estevaoam](https://github.com/estevaoam).
55
* [#115](https://github.com/intridea/grape-entity/pull/115): Allowing 'root' to be inherited from parent to child entities - [@guidoprincess](https://github.com/guidoprincess).
66
* [#121](https://github.com/intridea/grape-entity/pull/122): Sublcassed Entity#documentation properly handles unexposed params - [@dan-corneanu](https://github.com/dan-corneanu).
7+
* [#134](https://github.com/intridea/grape-entity/pull/134): Subclasses no longer affected in all cases by `unexpose` in parent - [@etehtsea](https://github.com/etehtsea).
78
* Your contribution here.
89

910
0.4.5 (2015-03-10)

lib/grape_entity/entity.rb

+25-69
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,26 @@ def entity(options = {})
9898
end
9999
end
100100

101+
class << self
102+
# Returns exposures that have been declared for this Entity or
103+
# ancestors. The keys are symbolized references to methods on the
104+
# containing object, the values are the options that were passed into expose.
105+
# @return [Hash] of exposures
106+
attr_accessor :exposures
107+
# Returns all formatters that are registered for this and it's ancestors
108+
# @return [Hash] of formatters
109+
attr_accessor :formatters
110+
attr_accessor :nested_attribute_names
111+
attr_accessor :nested_exposures
112+
end
113+
114+
def self.inherited(subclass)
115+
subclass.exposures = exposures.try(:dup) || {}
116+
subclass.nested_exposures = nested_exposures.try(:dup) || {}
117+
subclass.nested_attribute_names = nested_attribute_names.try(:dup) || {}
118+
subclass.formatters = formatters.try(:dup) || {}
119+
end
120+
101121
# This method is the primary means by which you will declare what attributes
102122
# should be exposed by the entity.
103123
#
@@ -141,14 +161,13 @@ def self.expose(*args, &block)
141161
args.each do |attribute|
142162
unless @nested_attributes.empty?
143163
orig_attribute = attribute.to_sym
144-
attribute = "#{@nested_attributes.last}__#{attribute}"
145-
nested_attribute_names_hash[attribute.to_sym] = orig_attribute
164+
attribute = "#{@nested_attributes.last}__#{attribute}".to_sym
165+
nested_attribute_names[attribute] = orig_attribute
146166
options[:nested] = true
147-
nested_exposures_hash[@nested_attributes.last.to_sym] ||= {}
148-
nested_exposures_hash[@nested_attributes.last.to_sym][attribute.to_sym] = options
167+
nested_exposures.deep_merge!(@nested_attributes.last.to_sym => { attribute => options })
149168
end
150169

151-
exposures[attribute.to_sym] = options
170+
exposures[attribute] = options
152171

153172
# Nested exposures are given in a block with no parameters.
154173
if block_given? && block.parameters.empty?
@@ -178,64 +197,12 @@ def self.with_options(options)
178197
@block_options.pop
179198
end
180199

181-
# Returns a hash of exposures that have been declared for this Entity or ancestors. The keys
182-
# are symbolized references to methods on the containing object, the values are
183-
# the options that were passed into expose.
184-
def self.exposures
185-
return @exposures unless @exposures.nil?
186-
187-
@exposures = {}
188-
189-
if superclass.respond_to? :exposures
190-
@exposures = superclass.exposures.merge(@exposures)
191-
end
192-
193-
@exposures
194-
end
195-
196-
class << self
197-
attr_accessor :_nested_attribute_names_hash
198-
attr_accessor :_nested_exposures_hash
199-
200-
def nested_attribute_names_hash
201-
self._nested_attribute_names_hash ||= {}
202-
end
203-
204-
def nested_exposures_hash
205-
self._nested_exposures_hash ||= {}
206-
end
207-
208-
def nested_attribute_names
209-
return @nested_attribute_names unless @nested_attribute_names.nil?
210-
211-
@nested_attribute_names = {}.merge(nested_attribute_names_hash)
212-
213-
if superclass.respond_to? :nested_attribute_names
214-
@nested_attribute_names = superclass.nested_attribute_names.deep_merge(@nested_attribute_names)
215-
end
216-
217-
@nested_attribute_names
218-
end
219-
220-
def nested_exposures
221-
return @nested_exposures unless @nested_exposures.nil?
222-
223-
@nested_exposures = {}.merge(nested_exposures_hash)
224-
225-
if superclass.respond_to? :nested_exposures
226-
@nested_exposures = superclass.nested_exposures.deep_merge(@nested_exposures)
227-
end
228-
229-
@nested_exposures
230-
end
231-
end
232-
233200
# Returns a hash, the keys are symbolized references to fields in the entity,
234201
# the values are document keys in the entity's documentation key. When calling
235202
# #docmentation, any exposure without a documentation key will be ignored.
236203
def self.documentation
237204
@documentation ||= exposures.each_with_object({}) do |(attribute, exposure_options), memo|
238-
unless exposure_options[:documentation].nil? || exposure_options[:documentation].empty?
205+
if exposure_options[:documentation].present?
239206
memo[key_for(attribute)] = exposure_options[:documentation]
240207
end
241208
end
@@ -272,17 +239,6 @@ def self.format_with(name, &block)
272239
formatters[name.to_sym] = block
273240
end
274241

275-
# Returns a hash of all formatters that are registered for this and it's ancestors.
276-
def self.formatters
277-
@formatters ||= {}
278-
279-
if superclass.respond_to? :formatters
280-
@formatters = superclass.formatters.merge(@formatters)
281-
end
282-
283-
@formatters
284-
end
285-
286242
# This allows you to set a root element name for your representation.
287243
#
288244
# @param plural [String] the root key to use when representing

spec/grape_entity/entity_spec.rb

+2-13
Original file line numberDiff line numberDiff line change
@@ -296,23 +296,12 @@ class Parent < Person
296296
expect(subject.exposures).to eq(name: {}, email: {})
297297
end
298298

299-
# the following 2 behaviors are testing because it is not most intuitive and could be confusing
300-
context 'when called from the parent class' do
301-
it 'remove from parent and all child classes that have not locked down their attributes with an .exposures call' do
299+
context 'when called from the parent class' do
300+
it 'remove from parent and do not remove from child classes' do
302301
subject.expose :name, :email
303302
child_class = Class.new(subject)
304303
subject.unexpose :email
305304

306-
expect(subject.exposures).to eq(name: {})
307-
expect(child_class.exposures).to eq(name: {})
308-
end
309-
310-
it 'remove from parent and do not remove from child classes that have locked down their attributes with an .exposures call' do
311-
subject.expose :name, :email
312-
child_class = Class.new(subject)
313-
child_class.exposures
314-
subject.unexpose :email
315-
316305
expect(subject.exposures).to eq(name: {})
317306
expect(child_class.exposures).to eq(name: {}, email: {})
318307
end

0 commit comments

Comments
 (0)