From 2786ae240d791878f03362be9b2c41a9914f47b6 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 09:28:00 +0000 Subject: [PATCH 01/11] - Add Victor::Component for component-driven SVG composition --- lib/victor.rb | 18 ++++++++++------- lib/victor/component.rb | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 lib/victor/component.rb diff --git a/lib/victor.rb b/lib/victor.rb index b25b21f..1a3d2ee 100644 --- a/lib/victor.rb +++ b/lib/victor.rb @@ -1,7 +1,11 @@ -require 'victor/version' -require 'victor/marshaling' -require 'victor/svg_base' -require 'victor/svg' -require 'victor/attributes' -require 'victor/css' -require 'victor/dsl' +autoload :VERSION, 'victor/version' + +module Victor + autoload :Attributes, 'victor/attributes' + autoload :Component, 'victor/component' + autoload :CSS, 'victor/css' + autoload :DSL, 'victor/dsl' + autoload :Marshaling, 'victor/marshaling' + autoload :SVG, 'victor/svg' + autoload :SVGBase, 'victor/svg_base' +end diff --git a/lib/victor/component.rb b/lib/victor/component.rb new file mode 100644 index 0000000..c0a6921 --- /dev/null +++ b/lib/victor/component.rb @@ -0,0 +1,45 @@ +module Victor + class Component + # Subclasses MUST implement this + def body = raise(NotImplementedError, "#{self.class.name} must implement `body'") + + # Subclasses MUST override these methods, OR assign instance vars + def height = @height || raise(NotImplementedError, "#{self.class.name} height or @height") + def width = @width || raise(NotImplementedError, "#{self.class.name} width or @width") + + # Subclasses MAY implement these methods + def style = {} + + # Subclasses MAY override these methods, OR assign instance vars + def x = @x ||= 0 + def y = @y ||= 0 + + # Rendering + def render = svg.render + alias to_s render + def save(...) = svg.save(...) + + # SVG + def vector = @vector ||= SVG.new(viewBox: "#{x} #{y} #{width} #{height}") + alias add vector + + # Appending/Embedding + def append(component) + vector.append component.svg + css.merge! component.style + end + alias embed append + + protected + + def css = @css ||= style.dup + + def svg + @svg ||= begin + body + vector.css = css + vector + end + end + end +end From eae3ac96dc835ac4dccfb1e07330456c32e760a9 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 13:32:03 +0000 Subject: [PATCH 02/11] refactor marshaling --- lib/victor/component.rb | 31 ++++++++++++++++--------------- lib/victor/marshaling.rb | 34 ++++++++++++---------------------- lib/victor/svg_base.rb | 4 ++++ spec/spec_helper.rb | 2 +- 4 files changed, 33 insertions(+), 38 deletions(-) diff --git a/lib/victor/component.rb b/lib/victor/component.rb index c0a6921..32f1fb6 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -1,5 +1,10 @@ module Victor class Component + include Marshaling + + # Marshaling data + def marshaling = %i[width height x y svg css] + # Subclasses MUST implement this def body = raise(NotImplementedError, "#{self.class.name} must implement `body'") @@ -7,10 +12,8 @@ def body = raise(NotImplementedError, "#{self.class.name} must implement `body'" def height = @height || raise(NotImplementedError, "#{self.class.name} height or @height") def width = @width || raise(NotImplementedError, "#{self.class.name} width or @width") - # Subclasses MAY implement these methods - def style = {} - # Subclasses MAY override these methods, OR assign instance vars + def style = @style ||= {} def x = @x ||= 0 def y = @y ||= 0 @@ -22,18 +25,6 @@ def save(...) = svg.save(...) # SVG def vector = @vector ||= SVG.new(viewBox: "#{x} #{y} #{width} #{height}") alias add vector - - # Appending/Embedding - def append(component) - vector.append component.svg - css.merge! component.style - end - alias embed append - - protected - - def css = @css ||= style.dup - def svg @svg ||= begin body @@ -41,5 +32,15 @@ def svg vector end end + + # CSS + def css = @css ||= style.dup + + # Appending/Embedding + def append(component) + vector.append component.svg + css.merge! component.css + end + alias embed append end end diff --git a/lib/victor/marshaling.rb b/lib/victor/marshaling.rb index 6c1771a..8d88f49 100644 --- a/lib/victor/marshaling.rb +++ b/lib/victor/marshaling.rb @@ -2,38 +2,28 @@ module Victor module Marshaling # YAML serialization methods def encode_with(coder) - coder['template'] = @template - coder['glue'] = @glue - coder['svg_attributes'] = @svg_attributes - coder['css'] = @css - coder['content'] = @content + marshaling.each do |attr| + coder[attr.to_s] = send(attr) + end end def init_with(coder) - @template = coder['template'] - @glue = coder['glue'] - @svg_attributes = coder['svg_attributes'] - @css = coder['css'] - @content = coder['content'] + marshaling.each do |attr| + instance_variable_set(:"@#{attr}", coder[attr.to_s]) + end end # Marshal serialization methods def marshal_dump - { - template: @template, - glue: @glue, - svg_attributes: @svg_attributes, - css: @css, - content: @content, - } + marshaling.to_h do |attr| + [attr, send(attr)] + end end def marshal_load(data) - @template = data[:template] - @glue = data[:glue] - @svg_attributes = data[:svg_attributes] - @css = data[:css] - @content = data[:content] + marshaling.each do |attr| + instance_variable_set(:"@#{attr}", data[attr]) + end end end end diff --git a/lib/victor/svg_base.rb b/lib/victor/svg_base.rb index 0f490c7..2a08b2b 100644 --- a/lib/victor/svg_base.rb +++ b/lib/victor/svg_base.rb @@ -12,6 +12,10 @@ def initialize(attributes = nil, &block) build(&block) if block end + def marshaling + %i[template glue svg_attributes css content] + end + def <<(additional_content) content.push additional_content.to_s end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3b4a10b..3c14b56 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,7 @@ SimpleCov.start do enable_coverage :branch if ENV['BRANCH_COV'] coverage_dir 'spec/coverage' - # track_files 'lib/**/*.rb' + track_files 'lib/**/*.rb' end end From 7e23c938d69f58f577cd1ff58379d14e98cf7e62 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 16:04:58 +0000 Subject: [PATCH 03/11] add specs --- lib/victor/component.rb | 20 ++++++-- lib/victor/marshaling.rb | 4 ++ spec/victor/component_spec.rb | 95 +++++++++++++++++++++++++++++++++++ spec/victor/svg_spec.rb | 2 +- 4 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 spec/victor/component_spec.rb diff --git a/lib/victor/component.rb b/lib/victor/component.rb index 32f1fb6..226eb60 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -6,11 +6,20 @@ class Component def marshaling = %i[width height x y svg css] # Subclasses MUST implement this - def body = raise(NotImplementedError, "#{self.class.name} must implement `body'") + def body + raise(NotImplementedError, "#{self.class.name} must implement `body'") + end # Subclasses MUST override these methods, OR assign instance vars - def height = @height || raise(NotImplementedError, "#{self.class.name} height or @height") - def width = @width || raise(NotImplementedError, "#{self.class.name} width or @width") + def height + @height || raise(NotImplementedError, + "#{self.class.name} must implement `height' or `@height'") + end + + def width + @width || raise(NotImplementedError, + "#{self.class.name} must implement `width' or `@width'") + end # Subclasses MAY override these methods, OR assign instance vars def style = @style ||= {} @@ -18,13 +27,14 @@ def x = @x ||= 0 def y = @y ||= 0 # Rendering - def render = svg.render - alias to_s render def save(...) = svg.save(...) + def render(...) = svg.render(...) + def to_s = render # SVG def vector = @vector ||= SVG.new(viewBox: "#{x} #{y} #{width} #{height}") alias add vector + def svg @svg ||= begin body diff --git a/lib/victor/marshaling.rb b/lib/victor/marshaling.rb index 8d88f49..ea6bc67 100644 --- a/lib/victor/marshaling.rb +++ b/lib/victor/marshaling.rb @@ -1,5 +1,9 @@ module Victor module Marshaling + def marshaling + raise NotImplementedError, "#{self.class.name} must implement `marshaling'" + end + # YAML serialization methods def encode_with(coder) marshaling.each do |attr| diff --git a/spec/victor/component_spec.rb b/spec/victor/component_spec.rb new file mode 100644 index 0000000..cdf7809 --- /dev/null +++ b/spec/victor/component_spec.rb @@ -0,0 +1,95 @@ +describe Victor::Component do + describe '#body' do + it 'raises a NotImplementedError' do + expect { subject.body }.to raise_error(NotImplementedError) + end + end + + describe '#height' do + it 'raises a NotImplementedError' do + expect { subject.height }.to raise_error(NotImplementedError) + end + end + + describe '#width' do + it 'raises a NotImplementedError' do + expect { subject.width }.to raise_error(NotImplementedError) + end + end + + describe '#style' do + it 'returns an empty hash' do + expect(subject.style).to eq({}) + end + end + + describe '#x' do + it 'returns 0' do + expect(subject.x).to eq 0 + end + end + + describe '#y' do + it 'returns 0' do + expect(subject.y).to eq 0 + end + end + + context 'when all required methods are implemented' do + before do + allow(subject).to receive(:body) + allow(subject).to receive(:width).and_return 100 + allow(subject).to receive(:height).and_return 100 + end + + describe '#save' do + it 'delegates to SVG' do + expect(subject.svg).to receive(:save).with('filename') + subject.save 'filename' + end + end + + describe '#render' do + it 'delegates to SVG' do + expect(subject.svg).to receive(:render).with(template: :minimal) + subject.render template: :minimal + end + end + + describe '#vector' do + it 'returns an SVG object' do + expect(subject.vector).to be_a Victor::SVG + end + end + + describe '#add' do + it 'is an alias to #vector' do + expect(subject.add).to equal subject.vector + end + end + + describe '#css' do + it 'returns a duplicate of #style' do + expect(subject.css).to eq subject.style + expect(subject.css).not_to equal subject.style + end + end + + describe '#append' do + let(:component) { double(:Component, svg: 'mocked_svg', css: { color: 'red' }) } + + it 'appends another component and merges its css' do + expect(subject.vector).to receive(:append).with('mocked_svg') + expect(subject.css).to receive(:merge!).with({ color: 'red' }) + + subject.append component + end + end + + describe '#embed' do + it 'is an alias to #append' do + expect(subject.method(:embed)).to eq subject.method(:append) + end + end + end +end diff --git a/spec/victor/svg_spec.rb b/spec/victor/svg_spec.rb index 83ce33d..1a9efda 100644 --- a/spec/victor/svg_spec.rb +++ b/spec/victor/svg_spec.rb @@ -1,5 +1,5 @@ describe Victor::SVG do - describe '#new' do + describe '#initialize' do it 'sets default attributes' do expect(subject.svg_attributes[:height]).to eq '100%' expect(subject.svg_attributes[:width]).to eq '100%' From 81feb50df6ec1b69ca00ea4a4182887f5f188e44 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 16:07:53 +0000 Subject: [PATCH 04/11] lint --- .rubocop.yml | 2 +- lib/victor/component.rb | 2 +- spec/victor/component_spec.rb | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 878fc63..916fa4a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,7 +16,7 @@ AllCops: TargetRubyVersion: 3.0 SuggestExtensions: false Exclude: - - 'dev/*' + - 'dev/**/*' # There is a special use case that needs this Lint/LiteralAsCondition: diff --git a/lib/victor/component.rb b/lib/victor/component.rb index 226eb60..ecb5aa0 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -20,7 +20,7 @@ def width @width || raise(NotImplementedError, "#{self.class.name} must implement `width' or `@width'") end - + # Subclasses MAY override these methods, OR assign instance vars def style = @style ||= {} def x = @x ||= 0 diff --git a/spec/victor/component_spec.rb b/spec/victor/component_spec.rb index cdf7809..0b39c39 100644 --- a/spec/victor/component_spec.rb +++ b/spec/victor/component_spec.rb @@ -37,9 +37,7 @@ context 'when all required methods are implemented' do before do - allow(subject).to receive(:body) - allow(subject).to receive(:width).and_return 100 - allow(subject).to receive(:height).and_return 100 + allow(subject).to receive_messages(body: nil, width: 100, height: 100) end describe '#save' do From 5930c6dbbcaf257c15bb9099c3a59604605e41d1 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 17:00:34 +0000 Subject: [PATCH 05/11] full coverage --- lib/victor.rb | 2 +- lib/victor/component.rb | 18 +++++----- spec/approvals/component/set1/render | 21 ++++++++++++ spec/fixtures/components/component_set1.rb | 21 ++++++++++++ spec/spec_helper.rb | 2 +- spec/victor/component_spec.rb | 40 +++++++++++----------- spec/victor/component_subclass_spec.rb | 11 ++++++ spec/victor/marshaling_spec.rb | 13 +++++++ 8 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 spec/approvals/component/set1/render create mode 100644 spec/fixtures/components/component_set1.rb create mode 100644 spec/victor/component_subclass_spec.rb create mode 100644 spec/victor/marshaling_spec.rb diff --git a/lib/victor.rb b/lib/victor.rb index 1a3d2ee..6891134 100644 --- a/lib/victor.rb +++ b/lib/victor.rb @@ -1,4 +1,4 @@ -autoload :VERSION, 'victor/version' +require 'victor/version' module Victor autoload :Attributes, 'victor/attributes' diff --git a/lib/victor/component.rb b/lib/victor/component.rb index ecb5aa0..5482f4f 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -29,12 +29,21 @@ def y = @y ||= 0 # Rendering def save(...) = svg.save(...) def render(...) = svg.render(...) + def content = svg.content def to_s = render + # Appending/Embedding + def append(component) + vector.append component.svg + css.merge! component.css + end + alias embed append + + protected + # SVG def vector = @vector ||= SVG.new(viewBox: "#{x} #{y} #{width} #{height}") alias add vector - def svg @svg ||= begin body @@ -45,12 +54,5 @@ def svg # CSS def css = @css ||= style.dup - - # Appending/Embedding - def append(component) - vector.append component.svg - css.merge! component.css - end - alias embed append end end diff --git a/spec/approvals/component/set1/render b/spec/approvals/component/set1/render new file mode 100644 index 0000000..bab4780 --- /dev/null +++ b/spec/approvals/component/set1/render @@ -0,0 +1,21 @@ + + + + + +Tada + + + diff --git a/spec/fixtures/components/component_set1.rb b/spec/fixtures/components/component_set1.rb new file mode 100644 index 0000000..a5569b4 --- /dev/null +++ b/spec/fixtures/components/component_set1.rb @@ -0,0 +1,21 @@ +module ComponentSet1 + class Base < Victor::Component + def width = 100 + def height = 100 + end + + class Main < Base + def body = append(Two.new) + def style = { '.one': { stroke: :magenta } } + end + + class Two < Base + def body = append(Three.new) + def style = { '.two': { stroke: :magenta } } + end + + class Three < Base + def body = add.text('Tada') + def style = { '.three': { stroke: :magenta } } + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3c14b56..3b4a10b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,7 @@ SimpleCov.start do enable_coverage :branch if ENV['BRANCH_COV'] coverage_dir 'spec/coverage' - track_files 'lib/**/*.rb' + # track_files 'lib/**/*.rb' end end diff --git a/spec/victor/component_spec.rb b/spec/victor/component_spec.rb index 0b39c39..46b276c 100644 --- a/spec/victor/component_spec.rb +++ b/spec/victor/component_spec.rb @@ -41,44 +41,44 @@ end describe '#save' do + let(:svg) { double save: true } + it 'delegates to SVG' do - expect(subject.svg).to receive(:save).with('filename') + allow(subject).to receive(:svg).and_return(svg) + expect(svg).to receive(:save).with('filename') subject.save 'filename' end end describe '#render' do + let(:svg) { double render: true } + it 'delegates to SVG' do - expect(subject.svg).to receive(:render).with(template: :minimal) + allow(subject).to receive(:svg).and_return(svg) + expect(svg).to receive(:render).with(template: :minimal) subject.render template: :minimal end end - describe '#vector' do - it 'returns an SVG object' do - expect(subject.vector).to be_a Victor::SVG - end - end - - describe '#add' do - it 'is an alias to #vector' do - expect(subject.add).to equal subject.vector - end - end + describe '#content' do + let(:svg) { double content: true } - describe '#css' do - it 'returns a duplicate of #style' do - expect(subject.css).to eq subject.style - expect(subject.css).not_to equal subject.style + it 'delegates to SVG' do + allow(subject).to receive(:svg).and_return(svg) + expect(svg).to receive(:content) + subject.content end end describe '#append' do - let(:component) { double(:Component, svg: 'mocked_svg', css: { color: 'red' }) } + let(:component) { double svg: 'mocked_svg', css: { color: 'red' } } + let(:vector) { double append: true } + let(:css) { double merge!: true } it 'appends another component and merges its css' do - expect(subject.vector).to receive(:append).with('mocked_svg') - expect(subject.css).to receive(:merge!).with({ color: 'red' }) + allow(subject).to receive_messages(vector: vector, css: css) + expect(vector).to receive(:append).with('mocked_svg') + expect(css).to receive(:merge!).with({ color: 'red' }) subject.append component end diff --git a/spec/victor/component_subclass_spec.rb b/spec/victor/component_subclass_spec.rb new file mode 100644 index 0000000..7c2eb27 --- /dev/null +++ b/spec/victor/component_subclass_spec.rb @@ -0,0 +1,11 @@ +require_relative '../fixtures/components/component_set1' + +describe 'Component subclassing' do + subject { ComponentSet1::Main.new } + + describe '#render' do + it 'returns the expected SVG' do + expect(subject.render).to match_approval 'component/set1/render' + end + end +end diff --git a/spec/victor/marshaling_spec.rb b/spec/victor/marshaling_spec.rb new file mode 100644 index 0000000..ee2a092 --- /dev/null +++ b/spec/victor/marshaling_spec.rb @@ -0,0 +1,13 @@ +describe Victor::Marshaling do + subject do + Class.new do + include Victor::Marshaling + end.new + end + + describe '#marshaling' do + it 'raises a NotImplementedError' do + expect { subject.marshaling }.to raise_error(NotImplementedError) + end + end +end From 96a8e89e5b7ec7379b1b61f3c466b969b891202f Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 17:32:32 +0000 Subject: [PATCH 06/11] update --- spec/approvals/component/set1/render | 5 +++++ spec/fixtures/components/component_set1.rb | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/spec/approvals/component/set1/render b/spec/approvals/component/set1/render index bab4780..b3629e8 100644 --- a/spec/approvals/component/set1/render +++ b/spec/approvals/component/set1/render @@ -14,8 +14,13 @@ } + + +Two + Tada + diff --git a/spec/fixtures/components/component_set1.rb b/spec/fixtures/components/component_set1.rb index a5569b4..775ee13 100644 --- a/spec/fixtures/components/component_set1.rb +++ b/spec/fixtures/components/component_set1.rb @@ -5,17 +5,24 @@ def height = 100 end class Main < Base - def body = append(Two.new) + def body + add.g transform: 'translate(10, 10)' do + append Two.new + end + end def style = { '.one': { stroke: :magenta } } end class Two < Base - def body = append(Three.new) + def body + add.text 'Two' + append Three.new + end def style = { '.two': { stroke: :magenta } } end class Three < Base - def body = add.text('Tada') + def body = add.text 'Tada' def style = { '.three': { stroke: :magenta } } end -end +end \ No newline at end of file From 2cb0072a21f36ac6e22ea6ddc72f211b79af7554 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Mon, 26 Aug 2024 17:40:15 +0000 Subject: [PATCH 07/11] fix ruby 3.0 tests --- spec/fixtures/components/component_set1.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/components/component_set1.rb b/spec/fixtures/components/component_set1.rb index 775ee13..8725b84 100644 --- a/spec/fixtures/components/component_set1.rb +++ b/spec/fixtures/components/component_set1.rb @@ -10,6 +10,7 @@ def body append Two.new end end + def style = { '.one': { stroke: :magenta } } end @@ -18,11 +19,15 @@ def body add.text 'Two' append Three.new end + def style = { '.two': { stroke: :magenta } } end class Three < Base - def body = add.text 'Tada' + def body + add.text 'Tada' + end + def style = { '.three': { stroke: :magenta } } end -end \ No newline at end of file +end From 440ff61845c2703c992e10748a66a77aeedc4585 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Tue, 27 Aug 2024 04:20:14 +0000 Subject: [PATCH 08/11] - Remove xlink from svg tag --- lib/victor/templates/default.svg | 6 +----- spec/approvals/component/set1/render | 6 +----- spec/approvals/svg/css | 6 +----- spec/approvals/svg/full | 6 +----- spec/approvals/svg/glue | 6 +----- 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/lib/victor/templates/default.svg b/lib/victor/templates/default.svg index 134e25e..4e1ac24 100644 --- a/lib/victor/templates/default.svg +++ b/lib/victor/templates/default.svg @@ -1,8 +1,4 @@ - - + %{style} %{content} - diff --git a/spec/approvals/component/set1/render b/spec/approvals/component/set1/render index b3629e8..c0e1289 100644 --- a/spec/approvals/component/set1/render +++ b/spec/approvals/component/set1/render @@ -1,7 +1,4 @@ - - + diff --git a/spec/approvals/svg/css b/spec/approvals/svg/css index ed8825d..2b8a386 100644 --- a/spec/approvals/svg/css +++ b/spec/approvals/svg/css @@ -1,7 +1,4 @@ - - + diff --git a/spec/approvals/svg/full b/spec/approvals/svg/full index 7ffd384..320ec88 100644 --- a/spec/approvals/svg/full +++ b/spec/approvals/svg/full @@ -1,9 +1,5 @@ - - + - diff --git a/spec/approvals/svg/glue b/spec/approvals/svg/glue index 9aaa8e3..3e15066 100644 --- a/spec/approvals/svg/glue +++ b/spec/approvals/svg/glue @@ -1,8 +1,4 @@ - - + - From 7b4024d659b292ff400c731249e8a21ef199878e Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Tue, 27 Aug 2024 17:42:50 +0000 Subject: [PATCH 09/11] update component internals --- lib/victor/component.rb | 32 +++++++++++++++++++------------- spec/victor/component_spec.rb | 12 ++++++------ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/victor/component.rb b/lib/victor/component.rb index 5482f4f..6fe3924 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -3,7 +3,7 @@ class Component include Marshaling # Marshaling data - def marshaling = %i[width height x y svg css] + def marshaling = %i[width height x y svg merged_css] # Subclasses MUST implement this def body @@ -32,27 +32,33 @@ def render(...) = svg.render(...) def content = svg.content def to_s = render - # Appending/Embedding + # Appending/Embedding - DSL for the `#body` implementation def append(component) - vector.append component.svg - css.merge! component.css + svg_instance.append component.svg + merged_css.merge! component.merged_css end alias embed append - protected - - # SVG - def vector = @vector ||= SVG.new(viewBox: "#{x} #{y} #{width} #{height}") - alias add vector + # SVG / CSS def svg @svg ||= begin body - vector.css = css - vector + svg_instance.css = merged_css + svg_instance end end - # CSS - def css = @css ||= style.dup + def css = @css ||= svg.css + + protected + + # Start with an ordinary SVG instance + def svg_instance = @svg_instance ||= SVG.new(viewBox: "#{x} #{y} #{width} #{height}") + + # Internal DSL to enable `add.anything` in the `#body` implementation + alias add svg_instance + + # Start with a copy of our own style + def merged_css = @merged_css ||= style.dup end end diff --git a/spec/victor/component_spec.rb b/spec/victor/component_spec.rb index 46b276c..0b4da2d 100644 --- a/spec/victor/component_spec.rb +++ b/spec/victor/component_spec.rb @@ -71,14 +71,14 @@ end describe '#append' do - let(:component) { double svg: 'mocked_svg', css: { color: 'red' } } - let(:vector) { double append: true } - let(:css) { double merge!: true } + let(:component) { double svg: 'mocked_svg', merged_css: { color: 'red' } } + let(:svg_instance) { double append: true } + let(:merged_css) { double merge!: true } it 'appends another component and merges its css' do - allow(subject).to receive_messages(vector: vector, css: css) - expect(vector).to receive(:append).with('mocked_svg') - expect(css).to receive(:merge!).with({ color: 'red' }) + allow(subject).to receive_messages(svg_instance: svg_instance, merged_css: merged_css) + expect(svg_instance).to receive(:append).with('mocked_svg') + expect(merged_css).to receive(:merge!).with({ color: 'red' }) subject.append component end From c38384af4f53ebe59ed65a24ab013bb5bebe1b1d Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Tue, 27 Aug 2024 18:01:10 +0000 Subject: [PATCH 10/11] switch to using direct delegation --- lib/victor/component.rb | 11 +++++------ spec/victor/component_spec.rb | 28 +++++++++++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/victor/component.rb b/lib/victor/component.rb index 6fe3924..2db7c3b 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -1,7 +1,12 @@ +require 'forwardable' + module Victor class Component + extend Forwardable include Marshaling + def_delegators :svg, :save, :render, :content, :element, :to_s + # Marshaling data def marshaling = %i[width height x y svg merged_css] @@ -26,12 +31,6 @@ def style = @style ||= {} def x = @x ||= 0 def y = @y ||= 0 - # Rendering - def save(...) = svg.save(...) - def render(...) = svg.render(...) - def content = svg.content - def to_s = render - # Appending/Embedding - DSL for the `#body` implementation def append(component) svg_instance.append component.svg diff --git a/spec/victor/component_spec.rb b/spec/victor/component_spec.rb index 0b4da2d..9be14e8 100644 --- a/spec/victor/component_spec.rb +++ b/spec/victor/component_spec.rb @@ -36,40 +36,50 @@ end context 'when all required methods are implemented' do + let(:svg) do + double save: true, render: true, content: true, element: true, to_s: true + end + before do allow(subject).to receive_messages(body: nil, width: 100, height: 100) + allow(subject).to receive(:svg).and_return(svg) end describe '#save' do - let(:svg) { double save: true } - it 'delegates to SVG' do - allow(subject).to receive(:svg).and_return(svg) expect(svg).to receive(:save).with('filename') subject.save 'filename' end end describe '#render' do - let(:svg) { double render: true } - it 'delegates to SVG' do - allow(subject).to receive(:svg).and_return(svg) expect(svg).to receive(:render).with(template: :minimal) subject.render template: :minimal end end describe '#content' do - let(:svg) { double content: true } - it 'delegates to SVG' do - allow(subject).to receive(:svg).and_return(svg) expect(svg).to receive(:content) subject.content end end + describe '#element' do + it 'delegates to SVG' do + expect(svg).to receive(:element).with(:rect) + subject.element :rect + end + end + + describe '#to_s' do + it 'delegates to SVG' do + expect(svg).to receive(:to_s) + subject.to_s + end + end + describe '#append' do let(:component) { double svg: 'mocked_svg', merged_css: { color: 'red' } } let(:svg_instance) { double append: true } From 79940d6dc90f17034f02039dad91eff0e818085a Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Tue, 27 Aug 2024 18:10:36 +0000 Subject: [PATCH 11/11] - Add `SVG#embed` as an alias to `SVG#append` --- lib/victor/component.rb | 4 +--- lib/victor/svg_base.rb | 1 + spec/victor/svg_spec.rb | 12 +++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/victor/component.rb b/lib/victor/component.rb index 2db7c3b..4f4bfba 100644 --- a/lib/victor/component.rb +++ b/lib/victor/component.rb @@ -5,7 +5,7 @@ class Component extend Forwardable include Marshaling - def_delegators :svg, :save, :render, :content, :element, :to_s + def_delegators :svg, :save, :render, :content, :element, :css, :to_s # Marshaling data def marshaling = %i[width height x y svg merged_css] @@ -47,8 +47,6 @@ def svg end end - def css = @css ||= svg.css - protected # Start with an ordinary SVG instance diff --git a/lib/victor/svg_base.rb b/lib/victor/svg_base.rb index 2a08b2b..1b9ead8 100644 --- a/lib/victor/svg_base.rb +++ b/lib/victor/svg_base.rb @@ -20,6 +20,7 @@ def <<(additional_content) content.push additional_content.to_s end alias append << + alias embed << def setup(attributes = nil) attributes ||= {} diff --git a/spec/victor/svg_spec.rb b/spec/victor/svg_spec.rb index 1a9efda..3c47cc3 100644 --- a/spec/victor/svg_spec.rb +++ b/spec/victor/svg_spec.rb @@ -58,12 +58,14 @@ end describe '#append' do - it 'pushes stringable objects as content' do - subject.append fire - subject.append earth - subject.append water + it 'is an alias to #<<' do + expect(subject.method(:append)).to eq subject.method(:<<) + end + end - expect(subject.to_s).to eq "\n\n" + describe '#embed' do + it 'is an alias to #<<' do + expect(subject.method(:embed)).to eq subject.method(:<<) end end end