meta_presenter 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2520562652ee736cdd7cca496683fb093e0b8f6ef5935429504b819feb61991f
4
- data.tar.gz: ab969767427ce4242ba5f70b144e277522d81c857b9393596dec839482c2aae9
3
+ metadata.gz: 8304ed69b8fcec17f9f190714e38d3d84e5dfc0747889303120c4d4eb77c8630
4
+ data.tar.gz: 2005c166ac8139c6e567c5e19c56ab5d38febc3a596e5a0f30edfd09074dcafb
5
5
  SHA512:
6
- metadata.gz: effeb19171c360f4bb06dcbef0779d60ef321c068a26544a941c33907b4875e5277eceb15dca065608ac4124aeafe00542dd1836ee6b10f3a22cfcb5c07a0d8d
7
- data.tar.gz: b4c3959db506ec00f8579e791dd0bf0f76d2955837f2524a256941bc4d2cd6422ca8338464f71a251f21e6f955518047970e9ea57d4cda10451faf16b046df52
6
+ metadata.gz: e6184fa5b49ecfba29fcc2825bd4dfcf82f83d4dcad33311bf9b9586cadeb2dfb1475796e1ca21db0bbf04a57dd8093454b88c3c91f41186c9fdfa9119226176
7
+ data.tar.gz: 4ff04576a64dd7c50670b74b5bbd2aa27c8a198000c5b3d2fd325b4806e0026bc2de595884818aa975d002c7537c9b91df9680134585dbdafe38374a29f54b44
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- meta_presenter (0.1.5)
4
+ meta_presenter (0.2.0)
5
5
  actionmailer (>= 4.0)
6
6
  actionpack (>= 4.0)
7
7
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![logo](https://user-images.githubusercontent.com/28652/50427588-2289cf80-087a-11e9-82e1-ae212adf0d07.png)](https://metapresenter.com)
4
4
 
5
- MetaPresenter is a Ruby gem that gives you access to the powerful presenter pattern in your Rails controllers. 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.
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
  ![overlay-shape-clean-sm](https://user-images.githubusercontent.com/28652/50854229-828c7580-1352-11e9-824b-a78c9a2404fb.png)
8
8
 
@@ -36,7 +36,7 @@ end
36
36
 
37
37
  ## Example
38
38
 
39
- Say you have a PagesController with an action for home and logs. Underneath `app/presenters` you can add a class for each action. In this example we'll also create an application and base presenter we'll inherit from to re-use code in the per-action presenters.
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
- {name: "Home", path: home_path},
125
- {name: "Logs", path: logs_path}
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.haml
147
+ app/views/pages/home.html.erb
148
148
 
149
- ```Haml
150
- %h1 Home
151
- %p #{greeting}
152
- %p Last login #{distance_of_time_in_words_to_now(last_login_at)}
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'meta_presenter'
5
- s.version = '0.1.6'
5
+ s.version = '0.2.0'
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.authors = ['szTheory']
8
8
  s.description = %q{Presenter pattern in your Rails controllers and actions}
@@ -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) { 'logs' }
6
+ let(:action_name) { 'test' }
8
7
  let(:object) { described_class.new(controller, action_name) }
9
8
 
10
9
  def controller_ancestors
@@ -2,4 +2,8 @@ require 'action_controller'
2
2
 
3
3
  class ApplicationController < ActionController::Base
4
4
  include MetaPresenter::Helpers
5
+
6
+ def a_method_defined_on_application_controller
7
+ "application controller method return value"
8
+ end
5
9
  end
@@ -1,2 +1,6 @@
1
1
  class PagesController < ApplicationController
2
+
3
+ def a_method_defined_on_pages_controller
4
+ "pages controller method return value"
5
+ end
2
6
  end
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.1.6
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-16 00:00:00.000000000 Z
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