meta_presenter 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +14 -12
- data/lib/meta_presenter/base/delegate_all_to.rb +1 -1
- data/lib/meta_presenter/helpers.rb +0 -9
- data/meta_presenter.gemspec +1 -1
- data/spec/meta_presenter/base/delegate_all_to_spec.rb +116 -0
- data/spec/meta_presenter/base/delegate_to_controller_spec.rb +58 -0
- data/spec/meta_presenter/base_spec.rb +19 -0
- data/spec/meta_presenter/builder_spec.rb +1 -2
- data/spec/support/app/controllers/application_controller.rb +4 -0
- data/spec/support/app/controllers/pages_controller.rb +4 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8304ed69b8fcec17f9f190714e38d3d84e5dfc0747889303120c4d4eb77c8630
|
4
|
+
data.tar.gz: 2005c166ac8139c6e567c5e19c56ab5d38febc3a596e5a0f30edfd09074dcafb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6184fa5b49ecfba29fcc2825bd4dfcf82f83d4dcad33311bf9b9586cadeb2dfb1475796e1ca21db0bbf04a57dd8093454b88c3c91f41186c9fdfa9119226176
|
7
|
+
data.tar.gz: 4ff04576a64dd7c50670b74b5bbd2aa27c8a198000c5b3d2fd325b4806e0026bc2de595884818aa975d002c7537c9b91df9680134585dbdafe38374a29f54b44
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://metapresenter.com)
|
4
4
|
|
5
|
-
MetaPresenter is a Ruby gem
|
5
|
+
MetaPresenter is a Ruby gem for writing highly focused and testable view Rails presenter classes. For each controller/action pair you get a presenter class in `app/presenters` that you can use in your views with with `presenter.method_name`. This helps you decompose your helper logic into tight, easily testable classes. There's even a DSL for method delegation on objects to reduce boilerplate.
|
6
6
|
|
7
7
|

|
8
8
|
|
@@ -36,7 +36,7 @@ end
|
|
36
36
|
|
37
37
|
## Example
|
38
38
|
|
39
|
-
Say you have a PagesController with
|
39
|
+
Say you have a PagesController with `#home` and `#logs` actions. Underneath app/presenters you can add a presenter class for each action (Pages::HomePresenter and Pages::LogsPresenter). We'll also create an ApplicationPresenter superclass for methods that can be used in any action throughout the app.
|
40
40
|
|
41
41
|
```
|
42
42
|
app/
|
@@ -113,7 +113,7 @@ class ApplicationPresenter < MetaPresenter::BasePresenter
|
|
113
113
|
end
|
114
114
|
```
|
115
115
|
|
116
|
-
app/presenters/pages_presenter.rb
|
116
|
+
app/presenters/pages_presenter.rb
|
117
117
|
|
118
118
|
```ruby
|
119
119
|
class PagesPresenter < ApplicationPresenter
|
@@ -121,8 +121,8 @@ class PagesPresenter < ApplicationPresenter
|
|
121
121
|
# all actions on PagesController
|
122
122
|
def nav_items
|
123
123
|
[
|
124
|
-
|
125
|
-
|
124
|
+
{name: "Home", path: home_path},
|
125
|
+
{name: "Logs", path: logs_path}
|
126
126
|
]
|
127
127
|
end
|
128
128
|
end
|
@@ -135,7 +135,7 @@ class Pages::HomePresenter < PagesPresenter
|
|
135
135
|
# presenter.email, presenter.id or any other
|
136
136
|
# method not already defined will delegate to
|
137
137
|
# the current_user
|
138
|
-
delegate_all_to :current_user
|
138
|
+
delegate_all_to = :current_user
|
139
139
|
|
140
140
|
# presenter.greeting in views
|
141
141
|
def greeting
|
@@ -144,12 +144,12 @@ class Pages::HomePresenter < PagesPresenter
|
|
144
144
|
end
|
145
145
|
```
|
146
146
|
|
147
|
-
app/views/pages/home.html.
|
147
|
+
app/views/pages/home.html.erb
|
148
148
|
|
149
|
-
```
|
150
|
-
|
151
|
-
|
152
|
-
|
149
|
+
```Erb
|
150
|
+
<h1>Home</h1>
|
151
|
+
<p><%= presenter.greeting %></p>
|
152
|
+
<p>Last login <%= distance_of_time_in_words_to_now(presenter.last_login_at) %></p>
|
153
153
|
```
|
154
154
|
|
155
155
|
app/presenters/pages/logs_presenter.rb
|
@@ -193,7 +193,7 @@ end
|
|
193
193
|
|
194
194
|
## Requirements
|
195
195
|
|
196
|
-
MetaPresenter supports Ruby >= 2.1 and ActionPack >= 4.0. If you'd like to help adding support for older versions please submit a pull request with passing specs.
|
196
|
+
MetaPresenter supports Ruby >= 2.1 and ActionPack/ActionMailer >= 4.0. If you'd like to help adding support for older versions please submit a pull request with passing specs.
|
197
197
|
|
198
198
|
## Links
|
199
199
|
|
@@ -207,6 +207,8 @@ To run the specs for the currently running Ruby version, run `bundle install` an
|
|
207
207
|
Make sure the specs pass, bump the version number in meta_presenter.gemspec, build the gem with `gem build meta_presenter.gemspec`. Then commit changes and tag the commit with the current release number with `git tag -a "vVersionNumberHere" -m "vVersionNumberHere"`. Push the commit with `git push`, then push the tags with `git push origin --tags`. Finally, push the gem with `gem push meta_presenter-version-number-here.gem`.
|
208
208
|
|
209
209
|
## TODO
|
210
|
+
* create an example app and link to the repo for it in this README
|
211
|
+
* proofread the README instructions to make sure everything is correct
|
210
212
|
* optional `rake meta_presenter:install` that generates the scaffolding for you. Or, you can manually create the files you want.
|
211
213
|
* add support for layout-level presenters
|
212
214
|
* add Rails 6 support once it comes out (hopefully just have to add a gemfiles/actionpack6.gemfile and it will run with the Appraisal suite)
|
@@ -12,7 +12,7 @@ module MetaPresenter
|
|
12
12
|
# all incoming methods send to the presenter that
|
13
13
|
# we not already handled by the presenter otherwise
|
14
14
|
# (such as explicitly defining the method)
|
15
|
-
class_attribute :delegate_all_to
|
15
|
+
class_attribute :delegate_all_to, instance_reader: false, instance_writer: false
|
16
16
|
|
17
17
|
include InstanceMethods
|
18
18
|
end
|
@@ -23,21 +23,12 @@ module MetaPresenter
|
|
23
23
|
helper_method :presenter
|
24
24
|
end
|
25
25
|
|
26
|
-
class PresenterClassNotFoundError < NameError
|
27
|
-
def initialize(controller, action_name)
|
28
|
-
super("No presenter class and method found for #{controller.class.name}##{action_name} (Are you rendering another action's template? If so then move your presenter method to a base presenter class for the controller that both action presenters inherit from)")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
26
|
private
|
33
27
|
# Initialize presenter with the current controller
|
34
28
|
def presenter
|
35
29
|
@presenter ||= begin
|
36
30
|
controller = self
|
37
31
|
klass = MetaPresenter::Builder.new(controller, action_name).presenter_class
|
38
|
-
if klass.nil?
|
39
|
-
raise PresenterClassNotFoundError.new(controller, action_name)
|
40
|
-
end
|
41
32
|
klass.new(controller)
|
42
33
|
end
|
43
34
|
end
|
data/meta_presenter.gemspec
CHANGED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MetaPresenter::Base::DelegateAllTo do
|
4
|
+
|
5
|
+
describe '.delegate_all_to' do
|
6
|
+
subject { presenter.send(method_name) }
|
7
|
+
|
8
|
+
let(:controller_class) { PagesController }
|
9
|
+
let(:controller) { controller_class.new }
|
10
|
+
let(:action_name) { 'logs' }
|
11
|
+
let(:presenter) { controller.view_context.presenter }
|
12
|
+
|
13
|
+
let(:controller_all_instance_methods) { controller.class.instance_methods(true) }
|
14
|
+
let(:presenter_all_instance_methods) { presenter.class.instance_methods(true) }
|
15
|
+
|
16
|
+
let(:delegate_object) { controller.send(delegate_object_method_name) }
|
17
|
+
let(:delegate_object_method_name) { :character }
|
18
|
+
|
19
|
+
before do
|
20
|
+
# stub action name on the controller
|
21
|
+
allow(controller).to receive(:action_name).and_return(action_name)
|
22
|
+
|
23
|
+
controller.class.redefine_method(delegate_object_method_name) do
|
24
|
+
OpenStruct.new(name: "Mario")
|
25
|
+
end
|
26
|
+
|
27
|
+
# set up the controller to delegate all to an object
|
28
|
+
presenter.class.delegate_all_to = delegate_object_method_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def define_method_on_presenter
|
32
|
+
presenter.class.redefine_method(method_name) do
|
33
|
+
"presenter retval"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_method_on_controller
|
38
|
+
controller.class.redefine_method(method_name) do
|
39
|
+
"controller retval"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "delegating a method the object responds to" do
|
44
|
+
let(:method_name) { :name }
|
45
|
+
|
46
|
+
before do
|
47
|
+
expect(delegate_object).to respond_to(method_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
context "and presenter doesn't define the method" do
|
51
|
+
before do
|
52
|
+
expect(presenter_all_instance_methods).to_not include(method_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
context "and controller doesn't define the method" do
|
56
|
+
before do
|
57
|
+
expect(controller_all_instance_methods).to_not include(method_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "calls the method on the delegated object" do
|
61
|
+
expect(subject).to eql(delegate_object.send(method_name))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "but controller defines the method" do
|
66
|
+
before do
|
67
|
+
define_method_on_controller
|
68
|
+
expect(controller_all_instance_methods).to include(method_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "still calls the method on the delegated object" do
|
72
|
+
expect(subject).to eql(delegate_object.send(method_name))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "but presenter also defines the method" do
|
78
|
+
before do
|
79
|
+
define_method_on_presenter
|
80
|
+
expect(presenter_all_instance_methods).to include(method_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "calls the presenter defined method" do
|
84
|
+
expect(subject).to eql("presenter retval")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "delegating a method the object doesn't respond to" do
|
90
|
+
let(:method_name) { :age }
|
91
|
+
|
92
|
+
before do
|
93
|
+
expect(delegate_object).to_not respond_to(method_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
context "and presenter doesn't define the method" do
|
97
|
+
before do
|
98
|
+
expect(presenter_all_instance_methods).to_not include(method_name)
|
99
|
+
end
|
100
|
+
|
101
|
+
it { expect { subject }.to raise_error(NoMethodError, "undefined method `#{method_name}' for #<Pages::LogsPresenter>") }
|
102
|
+
end
|
103
|
+
|
104
|
+
context "but presenter defines the method" do
|
105
|
+
before do
|
106
|
+
define_method_on_presenter
|
107
|
+
expect(presenter_all_instance_methods).to include(method_name)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "calls the presenter defined method" do
|
111
|
+
expect(subject).to eql("presenter retval")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MetaPresenter::Base::DelegateToController do
|
4
|
+
|
5
|
+
describe 'delegating a method to controller' do
|
6
|
+
subject { presenter.send(method_name) }
|
7
|
+
|
8
|
+
let(:controller_class) { PagesController }
|
9
|
+
let(:controller) { controller_class.new }
|
10
|
+
let(:action_name) { 'logs' }
|
11
|
+
let(:presenter) { controller.view_context.presenter }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(controller).to receive(:action_name).and_return(action_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:directly_defined_instance_methods) { controller.class.instance_methods(false) }
|
18
|
+
let(:inherited_instance_methods) { controller.class.instance_methods(true) }
|
19
|
+
|
20
|
+
context "method exists on the controller" do
|
21
|
+
before do
|
22
|
+
expect(controller).to respond_to(method_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "on the same class" do
|
26
|
+
let(:method_name) { :a_method_defined_on_pages_controller }
|
27
|
+
|
28
|
+
before do
|
29
|
+
expect(directly_defined_instance_methods).to include(method_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
it { is_expected.to eql("pages controller method return value") }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "on a superclass" do
|
36
|
+
let(:method_name) { :a_method_defined_on_application_controller }
|
37
|
+
|
38
|
+
before do
|
39
|
+
expect(directly_defined_instance_methods).to_not include(method_name)
|
40
|
+
expect(inherited_instance_methods).to include(method_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
it { is_expected.to eql("application controller method return value") }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "method doesn't exist on the controller" do
|
48
|
+
let(:method_name) { :a_method_not_defined_on_controller }
|
49
|
+
|
50
|
+
before do
|
51
|
+
expect(directly_defined_instance_methods).to_not include(method_name)
|
52
|
+
expect(inherited_instance_methods).to_not include(method_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
it { expect { subject }.to raise_error(NoMethodError) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MetaPresenter::Base do
|
4
|
+
|
5
|
+
describe '#inspect' do
|
6
|
+
subject { presenter.inspect }
|
7
|
+
|
8
|
+
let(:controller_class) { ApplicationController }
|
9
|
+
let(:controller) { controller_class.new }
|
10
|
+
let(:action_name) { 'test' }
|
11
|
+
let(:presenter) { controller.view_context.presenter }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(controller).to receive(:action_name).and_return(action_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
it { is_expected.to eql('#<ApplicationPresenter>') }
|
18
|
+
end
|
19
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
# require 'ostruct'
|
3
2
|
|
4
3
|
describe MetaPresenter::Builder do
|
5
4
|
let(:controller_class) { ApplicationController }
|
6
5
|
let(:controller) { controller_class.send(:new) }
|
7
|
-
let(:action_name) { '
|
6
|
+
let(:action_name) { 'test' }
|
8
7
|
let(:object) { described_class.new(controller, action_name) }
|
9
8
|
|
10
9
|
def controller_ancestors
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meta_presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- szTheory
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -163,6 +163,9 @@ files:
|
|
163
163
|
- lib/meta_presenter/helpers.rb
|
164
164
|
- logo.png
|
165
165
|
- meta_presenter.gemspec
|
166
|
+
- spec/meta_presenter/base/delegate_all_to_spec.rb
|
167
|
+
- spec/meta_presenter/base/delegate_to_controller_spec.rb
|
168
|
+
- spec/meta_presenter/base_spec.rb
|
166
169
|
- spec/meta_presenter/builder_spec.rb
|
167
170
|
- spec/meta_presenter/helpers_spec.rb
|
168
171
|
- spec/spec_helper.rb
|
@@ -209,6 +212,9 @@ summary: MetaPresenter is a Ruby gem that gives you access to the powerful prese
|
|
209
212
|
This helps you decompose your helper logic into small, tight, classes that are easily
|
210
213
|
testable. There's even a DSL for method delegation on objects to reduce boilerplate.
|
211
214
|
test_files:
|
215
|
+
- spec/meta_presenter/base/delegate_all_to_spec.rb
|
216
|
+
- spec/meta_presenter/base/delegate_to_controller_spec.rb
|
217
|
+
- spec/meta_presenter/base_spec.rb
|
212
218
|
- spec/meta_presenter/builder_spec.rb
|
213
219
|
- spec/meta_presenter/helpers_spec.rb
|
214
220
|
- spec/spec_helper.rb
|