diff --git a/lib/blueprinter/v2/association.rb b/lib/blueprinter/v2/association.rb index e0c5cac5..9fc5afe4 100644 --- a/lib/blueprinter/v2/association.rb +++ b/lib/blueprinter/v2/association.rb @@ -5,6 +5,7 @@ module V2 Association = Struct.new( :name, :blueprint, + :collection, :legacy_view, :from, :value_proc, diff --git a/lib/blueprinter/v2/dsl.rb b/lib/blueprinter/v2/dsl.rb index 096169f4..5ea8ba24 100644 --- a/lib/blueprinter/v2/dsl.rb +++ b/lib/blueprinter/v2/dsl.rb @@ -56,7 +56,7 @@ def field(name, from: name, **options, &definition) end # - # Define an association. + # Define an association to a single object. # # @param name [Symbol] Name of the association # @param blueprint [Class|Proc] Blueprint class to use, or one defined with a Proc @@ -65,12 +65,37 @@ def field(name, from: name, **options, &definition) # @yield [TODO] Generate the value from the block # @return [Blueprinter::V2::Association] # - def association(name, blueprint, from: name, view: nil, **options, &definition) + def object(name, blueprint, from: name, view: nil, **options, &definition) raise ArgumentError, 'The :view argument may not be used with V2 Blueprints' if view && blueprint.is_a?(V2) fields[name.to_sym] = Association.new( name: name, blueprint: blueprint, + collection: false, + legacy_view: view, + from: from, + value_proc: definition, + options: options.dup + ) + end + + # + # Define an association to a collection of objects. + # + # @param name [Symbol] Name of the association + # @param blueprint [Class|Proc] Blueprint class to use, or one defined with a Proc + # @param view [Symbol] Only for use with legacy (not V2) blueprints + # @param from [Symbol] Optionally specify a different method to call to get the value for "name" + # @yield [TODO] Generate the value from the block + # @return [Blueprinter::V2::Association] + # + def collection(name, blueprint, from: name, view: nil, **options, &definition) + raise ArgumentError, 'The :view argument may not be used with V2 Blueprints' if view && blueprint.is_a?(V2) + + fields[name.to_sym] = Association.new( + name: name, + blueprint: blueprint, + collection: true, legacy_view: view, from: from, value_proc: definition, diff --git a/lib/blueprinter/v2/reflection.rb b/lib/blueprinter/v2/reflection.rb index b1420b0e..30b1ff71 100644 --- a/lib/blueprinter/v2/reflection.rb +++ b/lib/blueprinter/v2/reflection.rb @@ -38,8 +38,10 @@ class View attr_reader :name # @return [Hash] Fields defined on the view attr_reader :fields - # @return [Hash] Associations defined on the view - attr_reader :associations + # @return [Hash] Associations to single objects defined on the view + attr_reader :objects + # @return [Hash] Associations to collections defined on the view + attr_reader :collections # @param blueprint [Class] A subclass of Blueprinter::V2::Base @@ -48,7 +50,8 @@ class View def initialize(blueprint, name) @name = name @fields = blueprint.fields.select { |_, f| f.is_a? Field } - @associations = blueprint.fields.select { |_, f| f.is_a? Association } + @objects = blueprint.fields.select { |_, f| f.is_a?(Association) && !f.collection } + @collections = blueprint.fields.select { |_, f| f.is_a?(Association) && f.collection } end end end diff --git a/spec/v2/fields_spec.rb b/spec/v2/fields_spec.rb index fc83ef42..21f2c4c9 100644 --- a/spec/v2/fields_spec.rb +++ b/spec/v2/fields_spec.rb @@ -26,23 +26,23 @@ category_blueprint = Class.new(Blueprinter::V2::Base) widget_blueprint = Class.new(Blueprinter::V2::Base) blueprint = Class.new(Blueprinter::V2::Base) do - association :category, category_blueprint - association :widgets, widget_blueprint, from: :foo, if: -> { true } - association(:foo, widget_blueprint) { {foo: "bar"} } + object :category, category_blueprint + collection :widgets, widget_blueprint, from: :foo, if: -> { true } + object(:foo, widget_blueprint) { {foo: "bar"} } end ref = blueprint.reflections[:default] - expect(ref.associations[:category].class.name).to eq "Blueprinter::V2::Association" - expect(ref.associations[:category].name).to eq :category - expect(ref.associations[:category].from).to eq :category - expect(ref.associations[:category].blueprint).to eq category_blueprint - expect(ref.associations[:widgets].name).to eq :widgets - expect(ref.associations[:widgets].from).to eq :foo - expect(ref.associations[:widgets].blueprint).to eq widget_blueprint - expect(ref.associations[:widgets].options[:if].class.name).to eq "Proc" - expect(ref.associations[:foo].name).to eq :foo - expect(ref.associations[:foo].blueprint).to eq widget_blueprint - expect(ref.associations[:foo].value_proc.class.name).to eq "Proc" + expect(ref.objects[:category].class.name).to eq "Blueprinter::V2::Association" + expect(ref.objects[:category].name).to eq :category + expect(ref.objects[:category].from).to eq :category + expect(ref.objects[:category].blueprint).to eq category_blueprint + expect(ref.collections[:widgets].name).to eq :widgets + expect(ref.collections[:widgets].from).to eq :foo + expect(ref.collections[:widgets].blueprint).to eq widget_blueprint + expect(ref.collections[:widgets].options[:if].class.name).to eq "Proc" + expect(ref.objects[:foo].name).to eq :foo + expect(ref.objects[:foo].blueprint).to eq widget_blueprint + expect(ref.objects[:foo].value_proc.class.name).to eq "Proc" end end @@ -97,8 +97,8 @@ blueprint = Class.new(Blueprinter::V2::Base) do field :id field :name - association :category, category_blueprint - association :widgets, widget_blueprint + object :category, category_blueprint + collection :widgets, widget_blueprint view :foo do exclude :name, :category @@ -108,9 +108,11 @@ refs = blueprint.reflections expect(refs[:default].fields.keys.sort).to eq %i(id name).sort - expect(refs[:default].associations.keys.sort).to eq %i(category widgets).sort + expect(refs[:default].objects.keys.sort).to eq %i(category).sort + expect(refs[:default].collections.keys.sort).to eq %i(widgets).sort expect(refs[:foo].fields.keys.sort).to eq %i(id description).sort - expect(refs[:foo].associations.keys.sort).to eq %i(widgets).sort + expect(refs[:foo].objects.keys.sort).to eq %i().sort + expect(refs[:foo].collections.keys.sort).to eq %i(widgets).sort end it "should exclude specified fields and associations from partials" do diff --git a/spec/v2/reflection_spec.rb b/spec/v2/reflection_spec.rb index 68f9f506..ec6d2840 100644 --- a/spec/v2/reflection_spec.rb +++ b/spec/v2/reflection_spec.rb @@ -69,18 +69,20 @@ widget_blueprint = Class.new(Blueprinter::V2::Base) blueprint = Class.new(Blueprinter::V2::Base) do field :name - association :category, category_blueprint + object :category, category_blueprint view :extended do field :description - association :widgets, widget_blueprint + collection :widgets, widget_blueprint end end expect(blueprint.reflections[:default].fields.keys).to eq %i(name) - expect(blueprint.reflections[:default].associations.keys).to eq %i(category) + expect(blueprint.reflections[:default].objects.keys).to eq %i(category) + expect(blueprint.reflections[:default].collections.keys).to eq %i() expect(blueprint.reflections[:extended].fields.keys).to eq %i(name description) - expect(blueprint.reflections[:extended].associations.keys).to eq %i(category widgets) + expect(blueprint.reflections[:extended].objects.keys).to eq %i(category) + expect(blueprint.reflections[:extended].collections.keys).to eq %i(widgets) end end