decent_presenter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +163 -0
- data/Rakefile +9 -0
- data/decent_presenter.gemspec +28 -0
- data/lib/decent_presenter/base.rb +29 -0
- data/lib/decent_presenter/collection_proxy.rb +23 -0
- data/lib/decent_presenter/exposable.rb +21 -0
- data/lib/decent_presenter/exposure.rb +41 -0
- data/lib/decent_presenter/factory.rb +22 -0
- data/lib/decent_presenter/version.rb +3 -0
- data/lib/decent_presenter.rb +10 -0
- data/spec/decent_presenter/base_spec.rb +80 -0
- data/spec/decent_presenter/collection_proxy_spec.rb +55 -0
- data/spec/decent_presenter/exposable_spec.rb +121 -0
- data/spec/decent_presenter/exposure_spec.rb +53 -0
- data/spec/decent_presenter/factory_spec.rb +33 -0
- data/spec/spec_helper.rb +9 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2f4b4c60cb311d3ae506f7c3ec534f0bda34f14e
|
4
|
+
data.tar.gz: 52a644f96700701a4da551807d9a53a480f305d7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a316414aff8b7247196552955ca115f47c9e2316f24daf8dd594257f7b0050ec7049640289a84fb5363b970286101e431dd0fb7e7c74c22db44f6df1853f7d46
|
7
|
+
data.tar.gz: 5cc0b55fc35ba29c714a238d87049be30f58cf5eb0d7e33aea8a56f08e7f3bf8f09274f27caef8f1196ef0cc5cfe7e88b54e9d4c69bc25f9851d5abd348ef0ff
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Karol Galanciak
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# DecentPresenter
|
2
|
+
|
3
|
+
Simple and easy to use presentation layer for your Rails models.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'decent_presenter'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install decent_presenter
|
18
|
+
|
19
|
+
## Basic usage
|
20
|
+
|
21
|
+
In your controller (ApplicationController) include DecentPresenter::Exposable module:
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
class ApplicationController < ActionController::Base
|
25
|
+
|
26
|
+
include DecentPresenter::Exposable
|
27
|
+
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
and you can start using DecentPresenters!
|
32
|
+
|
33
|
+
To decorate your model (e.g. user) just use:
|
34
|
+
|
35
|
+
`` ruby
|
36
|
+
@user = present(User.find(params[:id]))
|
37
|
+
```
|
38
|
+
|
39
|
+
Works the same way with collections:
|
40
|
+
|
41
|
+
`` ruby
|
42
|
+
@users = present(User.all)
|
43
|
+
```
|
44
|
+
|
45
|
+
By default it will use UserPresenter so it must be defined before decorating model. Put it in app/presenters directory. Presenters should inherit from DecentPresenter::Base:
|
46
|
+
|
47
|
+
``` ruby
|
48
|
+
class UserPresenter < DecentPresenter::Base
|
49
|
+
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
If you want other presenter, use with option:
|
54
|
+
|
55
|
+
`` ruby
|
56
|
+
@user = present(User.find(params[:id]), with: OtherUserPresenter)
|
57
|
+
```
|
58
|
+
|
59
|
+
or
|
60
|
+
|
61
|
+
`` ruby
|
62
|
+
@users = present(User.all, with: OtherUserPresenter)
|
63
|
+
```
|
64
|
+
|
65
|
+
To access decorated object within presenter use either model or object method:
|
66
|
+
|
67
|
+
|
68
|
+
``` ruby
|
69
|
+
class UserPresenter < DecentPresenter::Base
|
70
|
+
|
71
|
+
def name
|
72
|
+
"presented #{model.name}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def age
|
76
|
+
"presented #{object.name}"
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
You can access Rails helpers by h or helpers method:
|
83
|
+
|
84
|
+
``` ruby
|
85
|
+
class UserPresenter < DecentPresenter::Base
|
86
|
+
|
87
|
+
def edit_path
|
88
|
+
h.edit_user_path(model)
|
89
|
+
end
|
90
|
+
|
91
|
+
def path
|
92
|
+
helpers.user_path(model)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
All methods not implemented by presenters are automatically delegated to decorated model.
|
99
|
+
|
100
|
+
## Paginating collections?
|
101
|
+
|
102
|
+
Works with kaminari and will_paginate by default, no need to delegate and hack anything.
|
103
|
+
|
104
|
+
## How to decorate/present other objects within presenters?
|
105
|
+
|
106
|
+
The same way as in controllers:
|
107
|
+
|
108
|
+
``` ruby
|
109
|
+
|
110
|
+
class UserPresenter < DecentPresenter::Base
|
111
|
+
|
112
|
+
def articles
|
113
|
+
present(model.articles)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
```
|
119
|
+
|
120
|
+
## How do I test presenters?
|
121
|
+
|
122
|
+
This might be a bit tricky. To access helpers, presenters depend on view_context which is passed from controllers. To instantiate a new presenter, you need two arguments: model and view_context. If you don't need helpers, you can pass nil as a second argument:
|
123
|
+
|
124
|
+
``` ruby
|
125
|
+
presented_user = UserPresenter.new(user, nil)
|
126
|
+
```
|
127
|
+
|
128
|
+
What if you need helpers? You can pass ActionController::Base.helpers as a second argument. The problem with that solution is that you won't have access to url helpers from your routes and your custom helpers. To handle these cases, you can stub url methods or create fake helper module for your tests:
|
129
|
+
|
130
|
+
``` ruby
|
131
|
+
module FakeUrlHelpers
|
132
|
+
|
133
|
+
def articles_path
|
134
|
+
# some code
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
and extend helpers with these modules:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
helpers = ActionController::Base.helpers
|
144
|
+
helpers.extend FakeUrlHelpers
|
145
|
+
helpers.extend ApplicaitonHelper
|
146
|
+
|
147
|
+
presented_user = UserPresenter.new(user, helpers)
|
148
|
+
```
|
149
|
+
|
150
|
+
Future releases will handle such use cases in a cleaner way.
|
151
|
+
|
152
|
+
## TO-DO
|
153
|
+
|
154
|
+
* Implement generators
|
155
|
+
* Provide better solution for testing
|
156
|
+
|
157
|
+
## Contributing
|
158
|
+
|
159
|
+
1. Fork it ( https://github.com/[my-github-username]/decent_presenter/fork )
|
160
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
161
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
162
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
163
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'decent_presenter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "decent_presenter"
|
8
|
+
spec.version = DecentPresenter::VERSION
|
9
|
+
spec.authors = ["Karol Galanciak"]
|
10
|
+
spec.email = ["karol.galanciak@gmail.com"]
|
11
|
+
spec.summary = %q{Simple and easy to use presentation layer for your Rails models.}
|
12
|
+
spec.description = %q{Simple and easy to use presentation layer for your Rails models.}
|
13
|
+
spec.homepage = "https://github.com/Azdaroth/decent_presenter"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "guard"
|
25
|
+
spec.add_development_dependency "guard-rspec"
|
26
|
+
spec.add_development_dependency "coveralls"
|
27
|
+
spec.add_dependency "activesupport", ">= 3.2"
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "decent_presenter/exposable"
|
2
|
+
|
3
|
+
module DecentPresenter
|
4
|
+
class Base < SimpleDelegator
|
5
|
+
|
6
|
+
include DecentPresenter::Exposable
|
7
|
+
|
8
|
+
attr_reader :view_context
|
9
|
+
private :view_context
|
10
|
+
|
11
|
+
def initialize(object, view_context)
|
12
|
+
super(object)
|
13
|
+
@view_context = view_context
|
14
|
+
end
|
15
|
+
|
16
|
+
def model
|
17
|
+
__getobj__
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :object :model
|
21
|
+
|
22
|
+
def helpers
|
23
|
+
view_context
|
24
|
+
end
|
25
|
+
|
26
|
+
alias :h :helpers
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
|
3
|
+
|
4
|
+
module DecentPresenter
|
5
|
+
class CollectionProxy
|
6
|
+
|
7
|
+
delegate :current_page, :total_pages, :limit_value, :model_name, :total_count,
|
8
|
+
:total_entries, :per_page, :offset, to: :original_collection
|
9
|
+
|
10
|
+
attr_reader :original_collection, :presented_collection
|
11
|
+
private :original_collection, :presented_collection
|
12
|
+
|
13
|
+
def initialize(original_collection, presented_collection)
|
14
|
+
@original_collection = original_collection
|
15
|
+
@presented_collection = presented_collection
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(method, *args, &block)
|
19
|
+
presented_collection.send(method, *args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "decent_presenter/exposure"
|
2
|
+
|
3
|
+
module DecentPresenter
|
4
|
+
module Exposable
|
5
|
+
|
6
|
+
def present(presentable, options = {})
|
7
|
+
if respond_to? :view_context
|
8
|
+
DecentPresenter::Exposure.new(
|
9
|
+
view_context, DecentPresenter::Factory
|
10
|
+
).present(presentable, options)
|
11
|
+
else
|
12
|
+
raise DecentPresenter::Exposable::DoesNotImplementViewContextError.new(
|
13
|
+
"Object must implement :view_context method to handle presentation"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class DoesNotImplementViewContextError < StandardError ; end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "decent_presenter/collection_proxy"
|
2
|
+
|
3
|
+
module DecentPresenter
|
4
|
+
class Exposure
|
5
|
+
|
6
|
+
attr_reader :view_context, :presenter_factory
|
7
|
+
private :view_context, :presenter_factory
|
8
|
+
|
9
|
+
def initialize(view_context, presenter_factory)
|
10
|
+
@view_context = view_context
|
11
|
+
@presenter_factory = presenter_factory
|
12
|
+
end
|
13
|
+
|
14
|
+
def present(presentable, options = {})
|
15
|
+
if presentable_is_a_collection?(presentable)
|
16
|
+
present_collection(presentable, options)
|
17
|
+
else
|
18
|
+
present_model(presentable, options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def present_model(presentable, options = {})
|
25
|
+
presenter = options.fetch(:with) do
|
26
|
+
presenter_factory.presenter_for(presentable)
|
27
|
+
end
|
28
|
+
presenter.new(presentable, view_context)
|
29
|
+
end
|
30
|
+
|
31
|
+
def present_collection(collection, options = {})
|
32
|
+
presented_collection = collection.map { |el| present_model(el, options) }
|
33
|
+
DecentPresenter::CollectionProxy.new(collection, presented_collection)
|
34
|
+
end
|
35
|
+
|
36
|
+
def presentable_is_a_collection?(presentable)
|
37
|
+
[:size, :to_a, :first].all? { |method| presentable.respond_to? method }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module DecentPresenter
|
4
|
+
module Factory
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def presenter_for(model)
|
9
|
+
presenter_class_name = "#{model.class}Presenter"
|
10
|
+
begin
|
11
|
+
presenter_class_name.constantize
|
12
|
+
rescue NameError
|
13
|
+
raise PresenterForModelDoesNotExist.new(
|
14
|
+
"expected #{presenter_class_name} presenter to exist"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class PresenterForModelDoesNotExist < StandardError ; end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "decent_presenter/base"
|
2
|
+
require "decent_presenter/collection_proxy"
|
3
|
+
require "decent_presenter/exposable"
|
4
|
+
require "decent_presenter/exposure"
|
5
|
+
require "decent_presenter/factory"
|
6
|
+
require "decent_presenter/version"
|
7
|
+
|
8
|
+
module DecentPresenter
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyModelForBaseClassTest
|
4
|
+
|
5
|
+
def name
|
6
|
+
"dummy_model_name"
|
7
|
+
end
|
8
|
+
|
9
|
+
def stuff
|
10
|
+
"stuff"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class DummyViewContext ; end
|
16
|
+
|
17
|
+
|
18
|
+
class DummyModelPresenter < DecentPresenter::Base
|
19
|
+
|
20
|
+
def name
|
21
|
+
"presented #{model.name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
describe DecentPresenter::Base do
|
28
|
+
|
29
|
+
let(:model) { DummyModelForBaseClassTest.new }
|
30
|
+
let(:view_context) { DummyViewContext.new }
|
31
|
+
|
32
|
+
|
33
|
+
context "base" do
|
34
|
+
|
35
|
+
subject { DecentPresenter::Base.new(model, view_context) }
|
36
|
+
|
37
|
+
it "exposes model as model" do
|
38
|
+
expect(subject.model).to eq model
|
39
|
+
end
|
40
|
+
|
41
|
+
it "exposes model as object" do
|
42
|
+
expect(subject.object).to eq model
|
43
|
+
end
|
44
|
+
|
45
|
+
it "exposes view context as h" do
|
46
|
+
expect(subject.h).to eq view_context
|
47
|
+
end
|
48
|
+
|
49
|
+
it "exposes view context as helpers" do
|
50
|
+
expect(subject.helpers).to eq view_context
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context "subclass" do
|
56
|
+
|
57
|
+
subject { DummyModelPresenter.new(model, view_context) }
|
58
|
+
|
59
|
+
it "decorates model's methods" do
|
60
|
+
expect(subject.name).to eq "presented dummy_model_name"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "delegates method calls to model when the method is not defined
|
64
|
+
within presenter" do
|
65
|
+
expect(subject.stuff).to eq "stuff"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "raises error on model's instance when method is defined
|
69
|
+
neither in presenter nor in model" do
|
70
|
+
expect { subject.not_defined_method }.to raise_error
|
71
|
+
end
|
72
|
+
|
73
|
+
it "implements presentable interface" do
|
74
|
+
expect(subject).to respond_to :present
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
TEST_COLLECTION_PROXY_PAGINATION_METHODS = [
|
4
|
+
:current_page, :total_pages,
|
5
|
+
:limit_value, :model_name, :total_count,
|
6
|
+
:total_entries, :per_page, :offset
|
7
|
+
]
|
8
|
+
|
9
|
+
class DummyOriginalForCollectionProxy
|
10
|
+
|
11
|
+
TEST_COLLECTION_PROXY_PAGINATION_METHODS.each do |method|
|
12
|
+
define_method method do
|
13
|
+
"original"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class DummyPresentedForCollectionProxy
|
21
|
+
|
22
|
+
def presented_method
|
23
|
+
"presented"
|
24
|
+
end
|
25
|
+
|
26
|
+
def other_presented_method
|
27
|
+
"presented"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe DecentPresenter::CollectionProxy do
|
33
|
+
|
34
|
+
|
35
|
+
subject { DecentPresenter::CollectionProxy.new(
|
36
|
+
DummyOriginalForCollectionProxy.new, DummyPresentedForCollectionProxy.new
|
37
|
+
)
|
38
|
+
}
|
39
|
+
|
40
|
+
it "delegates pagination-related methods to original collection" do
|
41
|
+
|
42
|
+
TEST_COLLECTION_PROXY_PAGINATION_METHODS.each do |pagination_method|
|
43
|
+
expect(subject.send(pagination_method)).to eq "original"
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
it "delegates other methods to presented collection" do
|
50
|
+
[:presented_method, :other_presented_method].each do |presented_method|
|
51
|
+
expect(subject.send(presented_method)).to eq "presented"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyModelForExposableTest
|
4
|
+
|
5
|
+
def name
|
6
|
+
"name"
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class DummyModelForExposableTestPresenter < DecentPresenter::Base
|
12
|
+
|
13
|
+
def name
|
14
|
+
"presented name"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class DummyModelForExposableOtherPresenter < DecentPresenter::Base
|
20
|
+
|
21
|
+
def name
|
22
|
+
"other presented name"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class DummyObjectErrorPresenterExposable
|
28
|
+
|
29
|
+
include DecentPresenter::Exposable
|
30
|
+
|
31
|
+
def present_model(model)
|
32
|
+
present(model)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class DummyObjectPresenterExposable
|
38
|
+
|
39
|
+
|
40
|
+
def view_context ; end
|
41
|
+
|
42
|
+
include DecentPresenter::Exposable
|
43
|
+
|
44
|
+
def present_model(model)
|
45
|
+
present(model)
|
46
|
+
end
|
47
|
+
|
48
|
+
def present_model_with_options(model, options)
|
49
|
+
present(model, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def present_collection(collection)
|
54
|
+
present(collection)
|
55
|
+
end
|
56
|
+
|
57
|
+
def present_collection_with_options(collection, options)
|
58
|
+
present(collection, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
describe DecentPresenter::Exposable do
|
66
|
+
|
67
|
+
context "view_context prerequisite" do
|
68
|
+
|
69
|
+
it "raises DoesNotImplementViewContextError if view_context method
|
70
|
+
is not defined" do
|
71
|
+
model = DummyModelForExposableTest.new
|
72
|
+
expect do
|
73
|
+
DummyObjectErrorPresenterExposable.new.present_model(model)
|
74
|
+
end.to raise_error DecentPresenter::Exposable::DoesNotImplementViewContextError,
|
75
|
+
"Object must implement :view_context method to handle presentation"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "doesn't raise DoesNotImplementViewContextError if view_context method
|
79
|
+
is defined" do
|
80
|
+
model = DummyModelForExposableTest.new
|
81
|
+
expect do
|
82
|
+
DummyObjectPresenterExposable.new.present_model(model)
|
83
|
+
end.not_to raise_error
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
context "presentation" do
|
89
|
+
|
90
|
+
let(:model) { DummyModelForExposableTest.new }
|
91
|
+
let(:collection) { [model] }
|
92
|
+
|
93
|
+
subject { DummyObjectPresenterExposable.new }
|
94
|
+
|
95
|
+
it "presents model with default presenter" do
|
96
|
+
expect(subject.present_model(model).name).to eq "presented name"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "presents model with specified presenter" do
|
100
|
+
expect(subject.present_model_with_options(
|
101
|
+
model,
|
102
|
+
with: DummyModelForExposableOtherPresenter
|
103
|
+
).name
|
104
|
+
).to eq "other presented name"
|
105
|
+
end
|
106
|
+
|
107
|
+
it "presents models collection with default presenter" do
|
108
|
+
expect(subject.present_collection(collection).first.name).to eq "presented name"
|
109
|
+
end
|
110
|
+
|
111
|
+
it "presents models collection with specified presenter" do
|
112
|
+
expect(subject.present_collection_with_options(
|
113
|
+
collection,
|
114
|
+
with: DummyModelForExposableOtherPresenter
|
115
|
+
).first.name
|
116
|
+
).to eq "other presented name"
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyModelForExposureTest
|
4
|
+
|
5
|
+
def size ; end
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
class DummyModelForExposureTestPresenter < DecentPresenter::Base ; end
|
10
|
+
|
11
|
+
class DummyModelForExposureTestOtherPresenter < DecentPresenter::Base ; end
|
12
|
+
|
13
|
+
describe DecentPresenter::Exposure do
|
14
|
+
|
15
|
+
let(:presenter_factory) { double(:presenter_factory) }
|
16
|
+
let(:view_context) { double(:view_context) }
|
17
|
+
|
18
|
+
let(:model) { DummyModelForExposureTest.new }
|
19
|
+
let(:collection) { [model] }
|
20
|
+
|
21
|
+
subject { DecentPresenter::Exposure.new(view_context, presenter_factory) }
|
22
|
+
|
23
|
+
before(:each) do
|
24
|
+
allow(presenter_factory).to receive(:presenter_for)
|
25
|
+
.with(model) { DummyModelForExposureTestPresenter }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "presents model with default presenter" do
|
29
|
+
presented_model = subject.present(model)
|
30
|
+
expect(presented_model).to be_instance_of DummyModelForExposureTestPresenter
|
31
|
+
end
|
32
|
+
|
33
|
+
it "presents model with specified presenter" do
|
34
|
+
presented_model = subject.present(model, with: DummyModelForExposureTestOtherPresenter)
|
35
|
+
expect(presented_model).to be_instance_of DummyModelForExposureTestOtherPresenter
|
36
|
+
end
|
37
|
+
|
38
|
+
it "presents models in collection with default presenter" do
|
39
|
+
presented_collection = subject.present(collection)
|
40
|
+
expect(presented_collection.first).to be_instance_of DummyModelForExposureTestPresenter
|
41
|
+
end
|
42
|
+
|
43
|
+
it "presents models in collection with specified presenter" do
|
44
|
+
presented_collection = subject.present(collection, with: DummyModelForExposureTestOtherPresenter)
|
45
|
+
expect(presented_collection.first).to be_instance_of DummyModelForExposureTestOtherPresenter
|
46
|
+
end
|
47
|
+
|
48
|
+
it "wraps collection in CollectionProxy" do
|
49
|
+
presented_collection = subject.present(collection)
|
50
|
+
expect(presented_collection).to be_instance_of DecentPresenter::CollectionProxy
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyModelForFactoryPresenter ; end
|
4
|
+
class DummyModelForFactory ; end
|
5
|
+
class OtherDummyModel ; end
|
6
|
+
|
7
|
+
|
8
|
+
describe DecentPresenter::Factory do
|
9
|
+
|
10
|
+
subject { DecentPresenter::Factory }
|
11
|
+
|
12
|
+
it "implements DecentPresenter Factory interface" do
|
13
|
+
expect(subject).to respond_to :presenter_for
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".presenter_for" do
|
17
|
+
|
18
|
+
it "gives presenter class based on object's class in convention: KlassPresenter" do
|
19
|
+
model = DummyModelForFactory.new
|
20
|
+
expect(subject.presenter_for(model)).to eq DummyModelForFactoryPresenter
|
21
|
+
end
|
22
|
+
|
23
|
+
it "raises PresenterForModelDoesNotExist error if presenter class is not defined" do
|
24
|
+
model = OtherDummyModel.new
|
25
|
+
expect do
|
26
|
+
subject.presenter_for(model)
|
27
|
+
end.to raise_error DecentPresenter::Factory::PresenterForModelDoesNotExist,
|
28
|
+
"expected OtherDummyModelPresenter presenter to exist"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: decent_presenter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karol Galanciak
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activesupport
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.2'
|
111
|
+
description: Simple and easy to use presentation layer for your Rails models.
|
112
|
+
email:
|
113
|
+
- karol.galanciak@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".coveralls.yml"
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rspec"
|
121
|
+
- ".travis.yml"
|
122
|
+
- Gemfile
|
123
|
+
- Guardfile
|
124
|
+
- LICENSE.txt
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- decent_presenter.gemspec
|
128
|
+
- lib/decent_presenter.rb
|
129
|
+
- lib/decent_presenter/base.rb
|
130
|
+
- lib/decent_presenter/collection_proxy.rb
|
131
|
+
- lib/decent_presenter/exposable.rb
|
132
|
+
- lib/decent_presenter/exposure.rb
|
133
|
+
- lib/decent_presenter/factory.rb
|
134
|
+
- lib/decent_presenter/version.rb
|
135
|
+
- spec/decent_presenter/base_spec.rb
|
136
|
+
- spec/decent_presenter/collection_proxy_spec.rb
|
137
|
+
- spec/decent_presenter/exposable_spec.rb
|
138
|
+
- spec/decent_presenter/exposure_spec.rb
|
139
|
+
- spec/decent_presenter/factory_spec.rb
|
140
|
+
- spec/spec_helper.rb
|
141
|
+
homepage: https://github.com/Azdaroth/decent_presenter
|
142
|
+
licenses:
|
143
|
+
- MIT
|
144
|
+
metadata: {}
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options: []
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
requirements: []
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 2.2.2
|
162
|
+
signing_key:
|
163
|
+
specification_version: 4
|
164
|
+
summary: Simple and easy to use presentation layer for your Rails models.
|
165
|
+
test_files:
|
166
|
+
- spec/decent_presenter/base_spec.rb
|
167
|
+
- spec/decent_presenter/collection_proxy_spec.rb
|
168
|
+
- spec/decent_presenter/exposable_spec.rb
|
169
|
+
- spec/decent_presenter/exposure_spec.rb
|
170
|
+
- spec/decent_presenter/factory_spec.rb
|
171
|
+
- spec/spec_helper.rb
|
172
|
+
has_rdoc:
|