Skip to content

Commit

Permalink
Call it Blueprinter::V2::Base
Browse files Browse the repository at this point in the history
Signed-off-by: Jordan Hollinger <[email protected]>
  • Loading branch information
jhollinger committed Oct 15, 2024
1 parent a29cf5c commit f7beaa0
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 154 deletions.
115 changes: 1 addition & 114 deletions lib/blueprinter/v2.rb
Original file line number Diff line number Diff line change
@@ -1,116 +1,3 @@
# frozen_string_literal: true

require 'blueprinter/v2/dsl'
require 'blueprinter/v2/options'
require 'blueprinter/v2/reflection'
require 'blueprinter/v2/view_builder'

module Blueprinter
# Base class for V2 Blueprints
class V2
extend DSL
extend Reflection

class << self
# Options set on this Blueprint
attr_accessor :options
# Extensions set on this Blueprint
attr_accessor :extensions
# @api private The fully-qualified name, e.g. "MyBlueprint", or "MyBlueprint.foo.bar"
attr_accessor :blueprint_name
# @api private
attr_accessor :views, :fields, :excludes, :partials, :used_partials, :eval_mutex
end

self.views = ViewBuilder.new(self)
self.fields = {}
self.excludes = []
self.partials = {}
self.used_partials = []
self.extensions = []
self.options = Options.new(DEFAULT_OPTIONS)
self.blueprint_name = name
self.eval_mutex = Mutex.new

# Initialize subclass
def self.inherited(subclass)
subclass.views = ViewBuilder.new(subclass)
subclass.fields = fields.transform_values(&:dup)
subclass.excludes = []
subclass.partials = partials.dup
subclass.used_partials = []
subclass.extensions = extensions.dup
subclass.options = options.dup
subclass.blueprint_name = subclass.name || blueprint_name
subclass.eval_mutex = Mutex.new
end

# A descriptive name for the Blueprint view, e.g. "WidgetBlueprint.extended"
def self.inspect
blueprint_name
end

# A descriptive name for the Blueprint view, e.g. "WidgetBlueprint.extended"
def self.to_s
blueprint_name
end

# Append the sub-view name to blueprint_name
# @api private
def self.append_name(name)
self.blueprint_name = "#{blueprint_name}.#{name}"
end

#
# Access a child view.
#
# MyBlueprint[:extended]
# MyBlueprint["extended.plus"] or MyBlueprint[:extended][:plus]
#
# @param view [Symbol|String] Name of the view, e.g. :extended, "extended.plus"
# @return [Class] A descendent of Blueprinter::V2
#
def self.[](view)
eval! unless @evaled
view.to_s.split('.').reduce(self) do |blueprint, child|
blueprint.views[child.to_sym] ||
raise(Errors::UnknownView, "View '#{child}' could not be found in Blueprint '#{blueprint}'")
end
end

# Apply partials and field exclusions
# @api private
def self.eval!(lock = true)
return if @evaled

if lock
eval_mutex.synchronize { run_eval! unless @evaled }
else
run_eval!
end
end

# @api private
def self.run_eval!
used_partials.each do |name|
if !(p = partials[name])
raise Errors::UnknownPartial, "Partial '#{name}' could not be found in Blueprint '#{self}'"
end
class_eval(&p)
end

excludes.each { |f| fields.delete f }
@evaled = true
end

# Render the object
def self.render(obj, options = {})
new.render(obj, options)
end

# Render the object
def render(obj, options = {})
# TODO: call an external Render module/class, passing in self, obj, and options
end
end
end
require 'blueprinter/v2/base'
2 changes: 1 addition & 1 deletion lib/blueprinter/v2/association.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Blueprinter
class V2
module V2
Association = Struct.new(
:name,
:blueprint,
Expand Down
118 changes: 118 additions & 0 deletions lib/blueprinter/v2/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# frozen_string_literal: true

require 'blueprinter/v2/dsl'
require 'blueprinter/v2/options'
require 'blueprinter/v2/reflection'
require 'blueprinter/v2/view_builder'

module Blueprinter
# Base class for V2 Blueprints
module V2
class Base
extend DSL
extend Reflection

class << self
# Options set on this Blueprint
attr_accessor :options
# Extensions set on this Blueprint
attr_accessor :extensions
# @api private The fully-qualified name, e.g. "MyBlueprint", or "MyBlueprint.foo.bar"
attr_accessor :blueprint_name
# @api private
attr_accessor :views, :fields, :excludes, :partials, :used_partials, :eval_mutex
end

self.views = ViewBuilder.new(self)
self.fields = {}
self.excludes = []
self.partials = {}
self.used_partials = []
self.extensions = []
self.options = Options.new(DEFAULT_OPTIONS)
self.blueprint_name = name
self.eval_mutex = Mutex.new

# Initialize subclass
def self.inherited(subclass)
subclass.views = ViewBuilder.new(subclass)
subclass.fields = fields.transform_values(&:dup)
subclass.excludes = []
subclass.partials = partials.dup
subclass.used_partials = []
subclass.extensions = extensions.dup
subclass.options = options.dup
subclass.blueprint_name = subclass.name || blueprint_name
subclass.eval_mutex = Mutex.new
end

# A descriptive name for the Blueprint view, e.g. "WidgetBlueprint.extended"
def self.inspect
blueprint_name
end

# A descriptive name for the Blueprint view, e.g. "WidgetBlueprint.extended"
def self.to_s
blueprint_name
end

# Append the sub-view name to blueprint_name
# @api private
def self.append_name(name)
self.blueprint_name = "#{blueprint_name}.#{name}"
end

#
# Access a child view.
#
# MyBlueprint[:extended]
# MyBlueprint["extended.plus"] or MyBlueprint[:extended][:plus]
#
# @param view [Symbol|String] Name of the view, e.g. :extended, "extended.plus"
# @return [Class] A descendent of Blueprinter::V2::Base
#
def self.[](view)
eval! unless @evaled
view.to_s.split('.').reduce(self) do |blueprint, child|
blueprint.views[child.to_sym] ||
raise(Errors::UnknownView, "View '#{child}' could not be found in Blueprint '#{blueprint}'")
end
end

# Apply partials and field exclusions
# @api private
def self.eval!(lock = true)
return if @evaled

if lock
eval_mutex.synchronize { run_eval! unless @evaled }
else
run_eval!
end
end

# @api private
def self.run_eval!
used_partials.each do |name|
if !(p = partials[name])
raise Errors::UnknownPartial, "Partial '#{name}' could not be found in Blueprint '#{self}'"
end
class_eval(&p)
end

excludes.each { |f| fields.delete f }
@evaled = true
end

# Render the object
def self.render(obj, options = {})
new.render(obj, options)
end

# Render the object
def render(obj, options = {})
# TODO: call an external Render module/class, passing in self, obj, and options
end
end
end
end
2 changes: 1 addition & 1 deletion lib/blueprinter/v2/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require 'blueprinter/v2/field'

module Blueprinter
class V2
module V2
# Methods for defining Blueprint fields and views
module DSL
#
Expand Down
2 changes: 1 addition & 1 deletion lib/blueprinter/v2/field.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Blueprinter
class V2
module V2
Field = Struct.new(
:name,
:from,
Expand Down
2 changes: 1 addition & 1 deletion lib/blueprinter/v2/options.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Blueprinter
class V2
module V2
Options = Struct.new(
:exclude_nil,
keyword_init: true
Expand Down
4 changes: 2 additions & 2 deletions lib/blueprinter/v2/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require 'blueprinter/v2/field'

module Blueprinter
class V2
module V2
# API for reflecting on Blueprints
module Reflection
#
Expand Down Expand Up @@ -42,7 +42,7 @@ class View
attr_reader :associations


# @param blueprint [Class] A subclass of Blueprinter::V2
# @param blueprint [Class] A subclass of Blueprinter::V2::Base
# @param name [Symbol] Name of the view
# @api private
def initialize(blueprint, name)
Expand Down
4 changes: 2 additions & 2 deletions lib/blueprinter/v2/view_builder.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Blueprinter
class V2
module V2
#
# A Hash-like class that holds a Blueprint's views, but defers evaluation of their
# definitions until they're first accessed.
Expand All @@ -11,7 +11,7 @@ class V2
class ViewBuilder
include Enumerable

# @param parent [Class] A subclass of Blueprinter::V2
# @param parent [Class] A subclass of Blueprinter::V2::Base
def initialize(parent)
@parent = parent
@views = { default: parent }
Expand Down
10 changes: 5 additions & 5 deletions spec/v2/declarative_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe "Blueprinter::V2 Declarative API" do
it "should inherit fields defined after the view" do
blueprint = Class.new(Blueprinter::V2) do
blueprint = Class.new(Blueprinter::V2::Base) do
view :desc do
field :description
end
Expand All @@ -16,7 +16,7 @@
end

it "should include partials defined after the view" do
blueprint = Class.new(Blueprinter::V2) do
blueprint = Class.new(Blueprinter::V2::Base) do
field :name

view :foo do
Expand All @@ -33,7 +33,7 @@
end

it "should include partials defined after the use statement" do
blueprint = Class.new(Blueprinter::V2) do
blueprint = Class.new(Blueprinter::V2::Base) do
field :name
use :desc

Expand All @@ -47,7 +47,7 @@
end

it "should inherit when accessing views" do
blueprint = Class.new(Blueprinter::V2) do
blueprint = Class.new(Blueprinter::V2::Base) do
use :desc
field :name

Expand All @@ -69,7 +69,7 @@
end

it "should exclude fields added after the exclude statement" do
blueprint = Class.new(Blueprinter::V2) do
blueprint = Class.new(Blueprinter::V2::Base) do
field :id
field :name

Expand Down
Loading

0 comments on commit f7beaa0

Please sign in to comment.