Skip to content

Commit

Permalink
Bugfixes to accessing nested reflections
Browse files Browse the repository at this point in the history
Signed-off-by: Jordan Hollinger <[email protected]>
  • Loading branch information
jhollinger committed Aug 19, 2024
1 parent df35da7 commit aea8647
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 42 deletions.
7 changes: 1 addition & 6 deletions lib/blueprinter/v2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,13 @@ class << self
attr_accessor :views, :fields, :extensions, :options
# The fully-qualified name, e.g. "MyBlueprint", or "MyBlueprint.foo.bar"
attr_accessor :blueprint_name
# Name of the view, e.g. :default, :foo, :"foo.bar"
attr_accessor :view_name
end

self.views = {}
self.fields = {}
self.extensions = []
self.options = Options.new(DEFAULT_OPTIONS)
self.blueprint_name = name
self.view_name = :default

# Initialize subclass
def self.inherited(subclass)
Expand All @@ -32,7 +29,6 @@ def self.inherited(subclass)
subclass.extensions = extensions.dup
subclass.options = options.dup
subclass.blueprint_name = subclass.name || blueprint_name
subclass.view_name = subclass.name ? :default : view_name
end

# A descriptive name for the Blueprint view, e.g. "WidgetBlueprint.extended"
Expand All @@ -45,10 +41,9 @@ def self.to_s
blueprint_name
end

# Append the sub-view name to blueprint_name and view_name
# Append the sub-view name to blueprint_name
def self.append_name(name)
self.blueprint_name = "#{blueprint_name}.#{name}"
self.view_name = view_name == :default ? name : :"#{view_name}.#{name}"
end

#
Expand Down
25 changes: 15 additions & 10 deletions lib/blueprinter/v2/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class V2
module Reflection
def self.extended(klass)
klass.class_eval do
private_class_method :flattened_views
private_class_method :flatten_children
end
end

Expand All @@ -19,16 +19,21 @@ def self.extended(klass)
# @return [Hash<Symbol, Blueprinter::V2::Reflection::View>]
#
def reflections
@reflections ||= flattened_views(views)
@reflections ||= flatten_children(self, :default)
end

# Builds a flat Hash of nested views
def flattened_views(views, acc = {})
views.each_with_object(acc) do |(_, blueprint), obj|
obj[blueprint.view_name] = View.new(blueprint)
children = blueprint.views.except(:default)
flattened_views(children, obj)
end
def flatten_children(parent, child_name, path = [])
ref_key = path.empty? ? child_name : path.join('.').to_sym
child_view = parent.views.fetch(child_name)
child_ref = View.new(child_view, ref_key)

child_view.views.
except(:default).
reduce({ ref_key => child_ref }) do |acc, (name, _)|
children = flatten_children(child_view, name, path + [name])
acc.merge(children)
end
end

#
Expand All @@ -42,8 +47,8 @@ class View
# @return [Hash<Symbol, Blueprinter::V2::Association>] Associations defined on the view
attr_reader :associations

def initialize(blueprint)
@name = blueprint.view_name
def initialize(blueprint, name)
@name = name
@fields = blueprint.fields.select { |_, f| f.is_a? Field }
@associations = blueprint.fields.select { |_, f| f.is_a? Association }
end
Expand Down
13 changes: 0 additions & 13 deletions spec/v2/name_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ class NamedBlueprint < Blueprinter::V2
expect(NamedBlueprint.to_s).to eq "NamedBlueprint"
expect(NamedBlueprint.inspect).to eq "NamedBlueprint"
expect(NamedBlueprint.blueprint_name).to eq "NamedBlueprint"
expect(NamedBlueprint.view_name).to eq :default
end

it 'should find a view by name' do
expect(NamedBlueprint[:extended].to_s).to eq "NamedBlueprint.extended"
expect(NamedBlueprint[:extended].inspect).to eq "NamedBlueprint.extended"
expect(NamedBlueprint[:extended].blueprint_name).to eq "NamedBlueprint.extended"
expect(NamedBlueprint[:extended].view_name).to eq :extended
end

it 'should raise for an invalid view name' do
Expand All @@ -39,13 +37,11 @@ class NamedBlueprint < Blueprinter::V2
it 'should have no base name' do
expect(blueprint.to_s).to eq "MyBlueprint"
expect(blueprint.inspect).to eq "MyBlueprint"
expect(blueprint.view_name).to eq :default
end

it 'should find a view by name' do
expect(blueprint[:extended].to_s).to eq "MyBlueprint.extended"
expect(blueprint[:extended].inspect).to eq "MyBlueprint.extended"
expect(blueprint[:extended].view_name).to eq :extended
end
end

Expand All @@ -58,12 +54,10 @@ class NamedBlueprint < Blueprinter::V2

it 'should have no base name' do
expect(blueprint.blueprint_name).to eq "Blueprinter::V2"
expect(blueprint.view_name).to eq :default
end

it 'should find a view by name' do
expect(blueprint[:extended].blueprint_name).to eq "Blueprinter::V2.extended"
expect(blueprint[:extended].view_name).to eq :extended
end
end

Expand All @@ -82,27 +76,20 @@ class NamedBlueprint < Blueprinter::V2

it 'should find deeply nested names' do
expect(blueprint.blueprint_name).to eq "MyBlueprint"
expect(blueprint.view_name).to eq :default

expect(blueprint[:foo].blueprint_name).to eq "MyBlueprint.foo"
expect(blueprint[:foo].view_name).to eq :foo

expect(blueprint[:foo][:bar].blueprint_name).to eq "MyBlueprint.foo.bar"
expect(blueprint[:foo][:bar].view_name).to eq :"foo.bar"

expect(blueprint[:foo][:bar][:zorp].blueprint_name).to eq "MyBlueprint.foo.bar.zorp"
expect(blueprint[:foo][:bar][:zorp].view_name).to eq :"foo.bar.zorp"
end

it 'should find deeply nested names using dot syntax' do
expect(blueprint["foo"].blueprint_name).to eq "MyBlueprint.foo"
expect(blueprint["foo"].view_name).to eq :foo

expect(blueprint["foo.bar"].blueprint_name).to eq "MyBlueprint.foo.bar"
expect(blueprint["foo.bar"].view_name).to eq :"foo.bar"

expect(blueprint["foo.bar.zorp"].blueprint_name).to eq "MyBlueprint.foo.bar.zorp"
expect(blueprint["foo.bar.zorp"].view_name).to eq :"foo.bar.zorp"
end
end

Expand Down
49 changes: 36 additions & 13 deletions spec/v2/reflection_spec.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# frozen_string_literal: true

describe "Blueprinter::V2 Reflection" do
it "should find all view keys" do
blueprint = Class.new(Blueprinter::V2) do
let(:blueprint) do
Class.new(Blueprinter::V2) do
view :foo
view :bar do
view :foo do
view :borp
end
end
end
end

it "should find all view keys" do
view_names = blueprint.reflections.keys
expect(view_names.sort).to eq %i(
default
Expand All @@ -22,17 +24,8 @@
end

it "should find all view names" do
blueprint = Class.new(Blueprinter::V2) do
view :foo
view :bar do
view :foo do
view :borp
end
end
end

views = blueprint.reflections.values
expect(views.map(&:name).sort).to eq %i(
view_names = blueprint.reflections.values.map(&:name)
expect(view_names.sort).to eq %i(
default
foo
bar
Expand All @@ -41,6 +34,36 @@
).sort
end

it "should find nested view keys" do
bar_view_names = blueprint[:bar].reflections.keys
expect(bar_view_names.sort).to eq %i(
default
foo
foo.borp
).sort

bar_foo_view_names = blueprint[:"bar.foo"].reflections.keys
expect(bar_foo_view_names.sort).to eq %i(
default
borp
).sort
end

it "should find nested view names" do
bar_view_names = blueprint[:bar].reflections.values.map(&:name)
expect(bar_view_names.sort).to eq %i(
default
foo
foo.borp
).sort

bar_foo_view_names = blueprint[:"bar.foo"].reflections.values.map(&:name)
expect(bar_foo_view_names.sort).to eq %i(
default
borp
).sort
end

it "should find fields and associations" do
category_blueprint = Class.new(Blueprinter::V2)
widget_blueprint = Class.new(Blueprinter::V2)
Expand Down

0 comments on commit aea8647

Please sign in to comment.