Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow a V2 view to specify its parent, regardless of how it's nested #487

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/blueprinter/v2/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ module DSL
# Define a new child view, which is a subclass of self.
#
# @param name [Symbol] Name of the view
# @param inherit [Symbol] Name of view to inherit from (default is the immediate inherit)
# @yield Define the view in the block
#
def view(name, &definition)
def view(name, inherit: nil, &definition)
raise Errors::InvalidBlueprint, "View name may not contain '.'" if name.to_s =~ /\./

views[name.to_sym] = definition
views[name.to_sym] = ViewBuilder::Definition.new(definition, inherit)
end

#
Expand Down
24 changes: 20 additions & 4 deletions lib/blueprinter/v2/view_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module V2
class ViewBuilder
include Enumerable

Definition = Struct.new(:definition, :parent_override)

# @param parent [Class] A subclass of Blueprinter::V2::Base
def initialize(parent)
@parent = parent
Expand All @@ -23,7 +25,7 @@ def initialize(parent)
# Add a view definition.
#
# @param name [Symbol]
# @param definition [Proc]
# @param definition [Blueprinter::V2::ViewBuilder::Definition]
#
def []=(name, definition)
@pending[name.to_sym] = definition
Expand All @@ -37,13 +39,15 @@ def []=(name, definition)
#
def [](name)
name = name.to_sym
if [email protected]?(name) and @pending.key?(name)
if [email protected]?(name) and (pending = @pending[name])
parent = pending.parent_override ? root_blueprint[pending.parent_override] : @parent

@mut.synchronize do
next if @views.key?(name)

view = Class.new(@parent)
view = Class.new(parent)
view.append_name(name)
view.class_eval(&@pending[name]) if @pending[name]
view.class_eval(&pending.definition) if pending.definition
view.eval!(false)
@views[name] = view
end
Expand All @@ -63,6 +67,18 @@ def each(&block)
end
block ? enum.each(&block) : enum
end

private

# Finds and returns the "root" blueprint view class (i.e. one named :default).
# The view "inherit" option needs this so it can inherit using a fully-qualified view name.
def root_blueprint
return @root_blueprint if @root_blueprint

blueprint = @parent
blueprint = blueprint.superclass until blueprint.view_name == :default
@root_blueprint = blueprint
end
end
end
end
35 changes: 30 additions & 5 deletions spec/v2/view_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,24 @@

it "should store, but not evaluate, a view" do
calls = 0
builder[:foo] =
builder[:foo] = Blueprinter::V2::ViewBuilder::Definition.new(
proc do
field :description
calls += 1
end
)

expect(calls).to eq 0
end

it "should evaluate a view on first access" do
calls = 0
builder[:foo] =
builder[:foo] = Blueprinter::V2::ViewBuilder::Definition.new(
proc do
field :description
calls += 1
end
)

view = builder[:foo]
builder[:foo]
Expand All @@ -43,7 +45,7 @@
end

it "should fetch a view" do
builder[:foo] = proc { field :description }
builder[:foo] = Blueprinter::V2::ViewBuilder::Definition.new(proc { field :description })

view = builder.fetch(:foo)
expect(view.reflections[:default].fields.keys.sort).to eq %i(id name description).sort
Expand All @@ -54,10 +56,33 @@
end

it "should iterate over each view" do
builder[:foo] = proc { field :description }
builder[:bar] = proc { field :description }
builder[:foo] = Blueprinter::V2::ViewBuilder::Definition.new(proc { field :description })
builder[:bar] = Blueprinter::V2::ViewBuilder::Definition.new(proc { field :description })

keys = builder.each.map { |name, _| name }
expect(keys.sort).to eq %i(default foo bar).sort
end

it 'should inherit from a different parent' do
blueprint = Class.new(Blueprinter::V2::Base) do
view :normal do
field :foo
end

view :normal2, inherit: :normal do
field :foo2
end

view :expanded do
field :bar

view :foo, inherit: :normal do
field :foo2
end
end
end

expect(blueprint.reflections[:normal2].fields.keys).to eq %i(foo foo2)
expect(blueprint.reflections[:"expanded.foo"].fields.keys).to eq %i(foo foo2)
end
end