summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2017-01-09 21:45:49 +0100
committerRémy Coutable <remy@rymai.me>2017-01-18 16:38:34 +0100
commitbf789ff567c71ff68c216bfa8f3d43e09b6f49fb (patch)
tree28d4ac2aa74824eb5d85dc8271474980a3237bbe
parentfd72c0f4c748658f539d24a286366e9ac7a22b57 (diff)
downloadgitlab-ce-bf789ff567c71ff68c216bfa8f3d43e09b6f49fb.tar.gz
Improve presenter architecture
Signed-off-by: Rémy Coutable <remy@rymai.me>
-rw-r--r--app/presenters/build_presenter.rb15
-rw-r--r--app/presenters/ci/build/presenter.rb17
-rw-r--r--app/presenters/ci/variable/presenter.rb7
-rw-r--r--app/presenters/variable_presenter.rb5
-rw-r--r--lib/gitlab/view/presenter.rb32
-rw-r--r--lib/gitlab/view/presenter/base.rb26
-rw-r--r--lib/gitlab/view/presenter/delegated.rb19
-rw-r--r--lib/gitlab/view/presenter/simple.rb17
-rw-r--r--spec/lib/gitlab/view/presenter/base_spec.rb38
-rw-r--r--spec/lib/gitlab/view/presenter/delegated_spec.rb33
-rw-r--r--spec/lib/gitlab/view/presenter/simple_spec.rb33
-rw-r--r--spec/lib/gitlab/view/presenter_spec.rb29
-rw-r--r--spec/presenters/build_presenter_spec.rb35
-rw-r--r--spec/presenters/ci/build/presenter_spec.rb75
-rw-r--r--spec/presenters/ci/variable/presenter_spec.rb23
-rw-r--r--spec/presenters/variable_presenter_spec.rb23
16 files changed, 288 insertions, 139 deletions
diff --git a/app/presenters/build_presenter.rb b/app/presenters/build_presenter.rb
deleted file mode 100644
index 9c44a6d2dbe..00000000000
--- a/app/presenters/build_presenter.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class BuildPresenter < SimpleDelegator
- include Gitlab::View::Presenter
-
- presents :build
-
- def erased_by_user?
- # Build can be erased through API, therefore it does not have
- # `erase_by` user assigned in that case.
- erased? && erased_by
- end
-
- def self.ancestors
- super + [Ci::Build]
- end
-end
diff --git a/app/presenters/ci/build/presenter.rb b/app/presenters/ci/build/presenter.rb
new file mode 100644
index 00000000000..60392200fde
--- /dev/null
+++ b/app/presenters/ci/build/presenter.rb
@@ -0,0 +1,17 @@
+module Ci
+ class Build
+ class Presenter < Gitlab::View::Presenter::Delegated
+ presents :build
+
+ def erased_by_user?
+ # Build can be erased through API, therefore it does not have
+ # `erase_by` user assigned in that case.
+ erased? && erased_by
+ end
+
+ def erased_by_name
+ erased_by.name if erased_by
+ end
+ end
+ end
+end
diff --git a/app/presenters/ci/variable/presenter.rb b/app/presenters/ci/variable/presenter.rb
new file mode 100644
index 00000000000..02045e19cac
--- /dev/null
+++ b/app/presenters/ci/variable/presenter.rb
@@ -0,0 +1,7 @@
+module Ci
+ class Variable
+ class Presenter < Gitlab::View::Presenter::Simple
+ presents :variable
+ end
+ end
+end
diff --git a/app/presenters/variable_presenter.rb b/app/presenters/variable_presenter.rb
deleted file mode 100644
index 80382f3a001..00000000000
--- a/app/presenters/variable_presenter.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class VariablePresenter
- include Gitlab::View::Presenter
-
- presents :variable
-end
diff --git a/lib/gitlab/view/presenter.rb b/lib/gitlab/view/presenter.rb
deleted file mode 100644
index 8b63b271b99..00000000000
--- a/lib/gitlab/view/presenter.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module Gitlab
- module View
- module Presenter
- extend ActiveSupport::Concern
-
- included do
- include Gitlab::Routing
- include Gitlab::Allowable
- end
-
- def with_subject(subject)
- tap { @subject = subject }
- end
-
- def with_user(user)
- tap { @user = user }
- end
-
- private
-
- attr_reader :subject, :user
-
- class_methods do
- def presents(name)
- define_method(name) do
- subject
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb
new file mode 100644
index 00000000000..51e7ab064fe
--- /dev/null
+++ b/lib/gitlab/view/presenter/base.rb
@@ -0,0 +1,26 @@
+module Gitlab
+ module View
+ module Presenter
+ module Base
+ extend ActiveSupport::Concern
+
+ include Gitlab::Routing
+ include Gitlab::Allowable
+
+ attr_reader :subject
+
+ def can?(user, action)
+ super(user, action, subject)
+ end
+
+ private
+
+ class_methods do
+ def presents(name)
+ define_method(name) { subject }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/view/presenter/delegated.rb b/lib/gitlab/view/presenter/delegated.rb
new file mode 100644
index 00000000000..f4d330c590e
--- /dev/null
+++ b/lib/gitlab/view/presenter/delegated.rb
@@ -0,0 +1,19 @@
+module Gitlab
+ module View
+ module Presenter
+ class Delegated < SimpleDelegator
+ include Gitlab::View::Presenter::Base
+
+ def initialize(subject, **attributes)
+ @subject = subject
+
+ attributes.each do |key, value|
+ define_singleton_method(key) { value }
+ end
+
+ super(subject)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/view/presenter/simple.rb b/lib/gitlab/view/presenter/simple.rb
new file mode 100644
index 00000000000..b7653a0f3cc
--- /dev/null
+++ b/lib/gitlab/view/presenter/simple.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module View
+ module Presenter
+ class Simple
+ include Gitlab::View::Presenter::Base
+
+ def initialize(subject, **attributes)
+ @subject = subject
+
+ attributes.each do |key, value|
+ define_singleton_method(key) { value }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/view/presenter/base_spec.rb b/spec/lib/gitlab/view/presenter/base_spec.rb
new file mode 100644
index 00000000000..57b98276622
--- /dev/null
+++ b/spec/lib/gitlab/view/presenter/base_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe Gitlab::View::Presenter::Base do
+ let(:project) { double(:project) }
+ let(:presenter_class) do
+ Struct.new(:subject).include(described_class)
+ end
+
+ subject do
+ presenter_class.new(project)
+ end
+
+ describe '.presents' do
+ it 'exposes #subject with the given keyword' do
+ presenter_class.presents(:foo)
+
+ expect(subject.foo).to eq(project)
+ end
+ end
+
+ describe '#can?' do
+ let(:project) { create(:empty_project) }
+
+ context 'user is not allowed' do
+ it 'returns false' do
+ expect(subject.can?(nil, :read_project)).to be_falsy
+ end
+ end
+
+ context 'user is allowed' do
+ let(:project) { create(:empty_project, :public) }
+
+ it 'returns true' do
+ expect(subject.can?(nil, :read_project)).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/view/presenter/delegated_spec.rb b/spec/lib/gitlab/view/presenter/delegated_spec.rb
new file mode 100644
index 00000000000..816d6b7c6d4
--- /dev/null
+++ b/spec/lib/gitlab/view/presenter/delegated_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::View::Presenter::Delegated do
+ let(:project) { double(:project, foo: 'bar') }
+ let(:presenter_class) do
+ Class.new(described_class)
+ end
+
+ subject do
+ presenter_class.new(project)
+ end
+
+ it 'includes Gitlab::View::Presenter::Base' do
+ expect(described_class).to include(Gitlab::View::Presenter::Base)
+ end
+
+ describe '#initialize' do
+ subject do
+ presenter_class.new(project, user: 'user', foo: 'bar')
+ end
+
+ it 'takes arbitrary key/values and exposes them' do
+ expect(subject.user).to eq('user')
+ expect(subject.foo).to eq('bar')
+ end
+ end
+
+ describe 'delegation' do
+ it 'does not forward missing methods to subject' do
+ expect(subject.foo).to eq('bar')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/view/presenter/simple_spec.rb b/spec/lib/gitlab/view/presenter/simple_spec.rb
new file mode 100644
index 00000000000..baf074019ec
--- /dev/null
+++ b/spec/lib/gitlab/view/presenter/simple_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::View::Presenter::Simple do
+ let(:project) { double(:project) }
+ let(:presenter_class) do
+ Class.new(described_class)
+ end
+
+ subject do
+ presenter_class.new(project)
+ end
+
+ it 'includes Gitlab::View::Presenter::Base' do
+ expect(described_class).to include(Gitlab::View::Presenter::Base)
+ end
+
+ describe '#initialize' do
+ subject do
+ presenter_class.new(project, user: 'user', foo: 'bar')
+ end
+
+ it 'takes arbitrary key/values and exposes them' do
+ expect(subject.user).to eq('user')
+ expect(subject.foo).to eq('bar')
+ end
+ end
+
+ describe 'delegation' do
+ it 'does not forward missing methods to subject' do
+ expect { subject.foo }.to raise_error(NoMethodError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/view/presenter_spec.rb b/spec/lib/gitlab/view/presenter_spec.rb
deleted file mode 100644
index 0880fbe5d77..00000000000
--- a/spec/lib/gitlab/view/presenter_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::View::Presenter do
- let(:project) { double(:project, bar: 'baz!') }
- let(:presenter) do
- base_presenter = described_class
-
- Class.new do
- include base_presenter
-
- presents :foo
- end
- end
- subject do
- presenter.new.with_subject(project)
- end
-
- describe '#initialize' do
- it 'takes an object accessible via a reader' do
- expect(subject.foo).to eq(project)
- end
- end
-
- describe 'common helpers' do
- it 'responds to #can?' do
- expect(subject).to respond_to(:can?)
- end
- end
-end
diff --git a/spec/presenters/build_presenter_spec.rb b/spec/presenters/build_presenter_spec.rb
deleted file mode 100644
index fa7b0567622..00000000000
--- a/spec/presenters/build_presenter_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-
-describe BuildPresenter do
- let(:build) { create(:ci_build) }
- subject do
- described_class.new(build).with_subject(build)
- end
-
- describe '#initialize' do
- it 'takes a build and optional params' do
- expect { subject }.
- not_to raise_error
- end
-
- it 'exposes build' do
- expect(subject.build).to eq(build)
- end
-
- it 'forwards missing methods to build' do
- expect(subject.ref).to eq('master')
- end
- end
-
- describe '#erased_by_user?' do
- it 'takes a build and optional params' do
- expect(subject).not_to be_erased_by_user
- end
- end
-
- describe 'quack like a Ci::Build' do
- it 'takes a build and optional params' do
- expect(described_class.ancestors).to include(Ci::Build)
- end
- end
-end
diff --git a/spec/presenters/ci/build/presenter_spec.rb b/spec/presenters/ci/build/presenter_spec.rb
new file mode 100644
index 00000000000..ecab84dcbc9
--- /dev/null
+++ b/spec/presenters/ci/build/presenter_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe Ci::Build::Presenter do
+ let(:project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ subject do
+ described_class.new(build)
+ end
+
+ it 'inherits from Gitlab::View::Presenter::Delegated' do
+ expect(described_class.superclass).to eq(Gitlab::View::Presenter::Delegated)
+ end
+
+ describe '#initialize' do
+ it 'takes a build and optional params' do
+ expect { subject }.not_to raise_error
+ end
+
+ it 'exposes build' do
+ expect(subject.build).to eq(build)
+ end
+
+ it 'forwards missing methods to build' do
+ expect(subject.ref).to eq('master')
+ end
+ end
+
+ describe '#erased_by_user?' do
+ it 'takes a build and optional params' do
+ expect(subject).not_to be_erased_by_user
+ end
+ end
+
+ describe '#erased_by_name' do
+ context 'when build is not erased' do
+ before do
+ expect(build).to receive(:erased_by).and_return(nil)
+ end
+
+ it 'returns nil' do
+ expect(subject.erased_by_name).to be_nil
+ end
+ end
+ context 'when build is erased' do
+ before do
+ expect(build).to receive(:erased_by).twice.
+ and_return(double(:user, name: 'John Doe'))
+ end
+
+ it 'returns the name of the eraser' do
+ expect(subject.erased_by_name).to eq('John Doe')
+ end
+ end
+ end
+
+ describe 'quack like a Ci::Build permission-wise' do
+ context 'user is not allowed' do
+ let(:project) { create(:empty_project, public_builds: false) }
+
+ it 'returns false' do
+ expect(subject.can?(nil, :read_build)).to be_falsy
+ end
+ end
+
+ context 'user is allowed' do
+ let(:project) { create(:empty_project, :public) }
+
+ it 'returns true' do
+ expect(subject.can?(nil, :read_build)).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/presenters/ci/variable/presenter_spec.rb b/spec/presenters/ci/variable/presenter_spec.rb
new file mode 100644
index 00000000000..b3afb2e2e33
--- /dev/null
+++ b/spec/presenters/ci/variable/presenter_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Ci::Variable::Presenter do
+ let(:variable) { double(:variable) }
+
+ subject do
+ described_class.new(variable)
+ end
+
+ it 'inherits from Gitlab::View::Presenter::Simple' do
+ expect(described_class.superclass).to eq(Gitlab::View::Presenter::Simple)
+ end
+
+ describe '#initialize' do
+ it 'takes a variable and optional params' do
+ expect { subject }.not_to raise_error
+ end
+
+ it 'exposes variable' do
+ expect(subject.variable).to eq(variable)
+ end
+ end
+end
diff --git a/spec/presenters/variable_presenter_spec.rb b/spec/presenters/variable_presenter_spec.rb
deleted file mode 100644
index f09a0c922ae..00000000000
--- a/spec/presenters/variable_presenter_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'spec_helper'
-
-describe VariablePresenter do
- let(:variable) { double(:variable, foo: 'bar') }
- subject do
- described_class.new.with_subject(variable)
- end
-
- describe '#initialize' do
- it 'takes a variable and optional params' do
- expect { subject }.
- not_to raise_error
- end
-
- it 'exposes variable' do
- expect(subject.variable).to eq(variable)
- end
-
- it 'does not forward missing methods to variable' do
- expect { subject.foo }.to raise_error(NoMethodError)
- end
- end
-end