presenter_rails 1.0.1 → 2.0.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
  SHA1:
3
- metadata.gz: 07380e067bc7857407c563571ab083e00232e426
4
- data.tar.gz: 4b783155f31ff25861d81b4cee5041ae8e40ef0a
3
+ metadata.gz: ad881d965883dd21b21b96fca07691befd7aaa50
4
+ data.tar.gz: bbd8f56f09035195e9b469733f98188b281e8c37
5
5
  SHA512:
6
- metadata.gz: bbb4331e7032230fffdd8d84e7ab8e7f62b14017439b3f3b67633f6636e987799c5bc5da8e4b8e1f913208a6fe20c83cd5da935daf91cb528eb4683c8c09f077
7
- data.tar.gz: 3ac96ee5bf7174caa128c60ccb1c9d017026eda10beb4ac98f313b5ab073e7852af2d1f29c3ccbf94b7dfd651e2d180d7fc46ec429d451af658f819d579c39d1
6
+ metadata.gz: d0540e2d1c5e94a3aacb7ca87db48ec09f67c8c06bca8685e3f6bb501c49aec9e7c08b4599fa1a8882fe2c97f27b598f05a90ab25b29e4f46430c89c28d4a853
7
+ data.tar.gz: cb5fd596bc8178cc99362e555d0c3039b3448786279a702791d332fbf43be36f6517b4bcf1b5a7d3c6fa5b6719073904961b08ebbb60432de43286ce483df6fb
data/README.md CHANGED
@@ -1,41 +1,47 @@
1
- Presenter
1
+ Presenter [![Gem Version](https://badge.fury.io/rb/presenter_rails.svg)](http://badge.fury.io/rb/presenter_rails) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ElMassimo/presenter_rails/blob/master/LICENSE.txt)
2
2
  =====================
3
3
 
4
4
  Presenter helps you expose view models to your views in a convenient way, while
5
5
  still allowing you to define methods with the same name inside your controllers.
6
6
 
7
7
  ```ruby
8
- # app/controllers/person_controller.rb
9
- class PersonController < ApplicationController
8
+ # app/controllers/people_controller.rb
9
+ class PeopleController < ApplicationController
10
10
 
11
- present :person do
12
- PersonPresenter.decorate(...)
13
- end
14
- end
11
+ present(:person) {
12
+ PersonDecorator.decorate(person)
13
+ }
14
+
15
+ ...
16
+
17
+ def person
18
+ People.find(params[:id])
19
+ end
20
+ end
15
21
  ```
16
22
 
17
23
  ```haml
18
- / app/views/people/show.html.haml
19
- .person
20
- .person-name= person.name
21
- .person-info= person.biography
24
+ / app/views/people/show.html.haml
25
+ .person
26
+ .person-name= person.name
27
+ .person-info= person.biography
22
28
  ```
23
- If you don't provide a block for present, it will assume that you want to expose a `"#{name}_presenter"` method.
29
+
30
+ The method is also available in the controller, with a `_presenter` suffix:
24
31
  ```ruby
25
- # app/controllers/person_controller.rb
26
- class PersonController < ApplicationController
27
- present :person, :people
32
+ # app/controllers/people_controller.rb
33
+ class PeopleController < ApplicationController
34
+
35
+ ...
28
36
 
29
- private
37
+ def update
38
+ person.update(attrs)
39
+ redirect_to person_presenter.path, notice: "Successfully updated."
40
+ end
30
41
 
31
- def person_presenter
32
- PersonPresenter.decorate(...)
33
- end
42
+ ...
34
43
 
35
- def people_presenter
36
- People.all
37
- end
38
- end
44
+ end
39
45
  ```
40
46
 
41
47
  ## Background
@@ -45,12 +51,12 @@ about what you are exposing, although it's specially useful to implement [two-st
45
51
 
46
52
  ### How it works
47
53
 
48
- When you provide a block, it defines a `"#{name}_presenter"` private method in your controller the same way you would do manually.
54
+ When you provide a block, it defines a `"#{name}_presenter"` private method in your controller.
49
55
 
50
- After that, it creates helper methods for your views, each method calls its `"#{name}_presenter"` counterpart in the controller.
56
+ After that, it creates a helper method for your views, which calls the `"#{name}_presenter"` counterpart in the controller.
51
57
 
52
58
  #### Memoization
53
- Each presenter method is memoized, so the method is called only once and your views get the same instance every time.
59
+ Each presenter method is memoized, so the method is called only once and your views get the same instance every time. The block is evaluated only if the method is called.
54
60
 
55
61
  #### Corolary
56
62
  Since the helper methods defined are only available for the view, you can define methods with the same name in your controller :smiley:.
@@ -1,5 +1,24 @@
1
- require 'presenter_rails/presenter'
1
+ require "presenter_rails/version"
2
+ require "active_support/all"
2
3
 
3
- ActiveSupport.on_load(:action_controller) do
4
- include PresenterRails::Presenter
4
+ module PresenterRails
5
+ autoload :Controller, "presenter_rails/controller"
6
+
7
+ # Internal: Name of the presenter method as defined in the controller.
8
+ def self.method_name_for(name)
9
+ "#{ name }_presenter"
10
+ end
11
+
12
+ # Internal: Name of the instance variable used to memoize a presenter method.
13
+ def self.ivar_for(name)
14
+ "@_presenter_#{ name.to_s.gsub("?", "_query").gsub("!", "_bang") }"
15
+ end
16
+
17
+ ActiveSupport.on_load :action_controller do
18
+ extend Controller
19
+ end
20
+
21
+ ActiveSupport.on_load :action_mailer do
22
+ extend Controller
23
+ end
5
24
  end
@@ -0,0 +1,26 @@
1
+ module PresenterRails
2
+ module Controller
3
+ # Public: Defines a method and makes it available to the view context
4
+ # under the specified name as a memoized variable.
5
+ #
6
+ # name - The name of the method as called from the view context.
7
+ # block - Executed once if (and only if) the method is called.
8
+ #
9
+ # Returns nothing.
10
+ def present(name, &block)
11
+ presenter_method = PresenterRails.method_name_for(name)
12
+ ivar = PresenterRails.ivar_for(name)
13
+
14
+ private define_method(presenter_method) {
15
+ unless instance_variable_defined?(ivar)
16
+ instance_variable_set(ivar, instance_exec(&block))
17
+ end
18
+ instance_variable_get(ivar)
19
+ }
20
+
21
+ helper Module.new {
22
+ define_method(name) { controller.send(presenter_method) }
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module PresenterRails
2
+ VERSION = "2.0.0"
3
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+ require "support/rails_app"
3
+ require "rspec/rails"
4
+
5
+ RSpec.describe BirdsController, type: :controller do
6
+ Given(:bird) { double(:bird) }
7
+
8
+ context "present works well with the view context" do
9
+ Given {
10
+ class BirdsController
11
+ present(:bird) { bird }
12
+ present(:bird?) { bird_presenter.present? }
13
+ end
14
+
15
+ expect_any_instance_of(BirdsController).to receive(:bird).once.and_return(bird)
16
+ }
17
+ When { get :index }
18
+ Then { controller.view_context.bird == bird }
19
+ And { controller.view_context.bird == controller.send(:bird_presenter) }
20
+ And { controller.view_context.bird? == true }
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+ require "support/rails_app"
3
+ require "rspec/rails"
4
+ require "action_mailer"
5
+
6
+ class BirdsMailer < ActionMailer::Base
7
+ present(:bird) { OpenStruct.new(name: @name) }
8
+
9
+ def hello_bird(name:)
10
+ @name = name
11
+ mail { |format| format.text { render plain: bird_presenter.name } }
12
+ end
13
+ end
14
+
15
+ RSpec.describe BirdsMailer, type: :mailer do
16
+ Given(:name) { "pidgeon" }
17
+
18
+ context "with keyword arguments" do
19
+ When(:mail) { BirdsMailer.hello_bird(name: name) }
20
+ Then { mail.body.to_s == name }
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+ require "rspec/rails"
3
+
4
+ RSpec.describe PresenterRails::Controller do
5
+ class Thing; end
6
+ class DifferentThing; end
7
+
8
+ class ViewContext
9
+ attr_reader :controller
10
+
11
+ def initialize(controller)
12
+ @controller = controller
13
+ end
14
+ end
15
+
16
+ class BaseController
17
+ extend PresenterRails::Controller
18
+
19
+ def self.helper(helper_module)
20
+ @helper_modules ||= []
21
+ @helper_modules << helper_module
22
+ end
23
+
24
+ def self.helper_modules
25
+ @helper_modules
26
+ end
27
+
28
+ def view_context
29
+ self.class.helper_modules.inject(ViewContext.new(self)) { |view_context, helper_module|
30
+ view_context.extend(helper_module)
31
+ }
32
+ end
33
+ end
34
+
35
+ Given(:controller_class) { Class.new(BaseController) }
36
+ Given(:controller) { controller_class.new }
37
+ Given(:view_context) { controller.view_context }
38
+ Given(:helper_modules) { controller_class.helper_modules }
39
+
40
+ delegate :present, to: :controller_class
41
+
42
+ context "present" do
43
+ Given {
44
+ expect(controller).to receive(:thing).once.and_return(Thing.new)
45
+ expect(controller).to receive(:different_thing).once.and_return(DifferentThing.new)
46
+ expect(controller).to receive(:empty).once.and_return(nil)
47
+ }
48
+
49
+ Given {
50
+ present(:thing) { thing }
51
+ present(:another_thing) { different_thing }
52
+ present(:empty?) { empty }
53
+ }
54
+
55
+ Then { controller.send(:thing_presenter).is_a?(Thing) }
56
+ And { controller.send(:another_thing_presenter).is_a?(DifferentThing) }
57
+ And { controller.send(:'empty?_presenter').nil? }
58
+
59
+ And { helper_modules.size == 3 }
60
+ And { helper_modules[0].instance_methods == [:thing] }
61
+ And { helper_modules[1].instance_methods == [:another_thing] }
62
+ And { helper_modules[2].instance_methods == [:empty?] }
63
+
64
+ And { view_context.thing.is_a?(Thing) }
65
+ And { view_context.another_thing.is_a?(DifferentThing) }
66
+ And { view_context.empty?.nil? }
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ require "presenter_rails"
2
+ require "rspec/given"
3
+ require "pry-byebug"
@@ -0,0 +1,41 @@
1
+ require "action_controller"
2
+ require "rails"
3
+
4
+ def request_params(params)
5
+ return params if Rails::VERSION::MAJOR < 5
6
+ { params: params }
7
+ end
8
+
9
+ module Rails
10
+ class App
11
+ def env_config
12
+ {}
13
+ end
14
+
15
+ def routes
16
+ @routes ||= ActionDispatch::Routing::RouteSet.new.tap do |routes|
17
+ routes.draw do
18
+ resources :birds
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.root
25
+ ''
26
+ end
27
+
28
+ def self.application
29
+ @app ||= App.new
30
+ end
31
+ end
32
+
33
+ class ApplicationController < ActionController::Base
34
+ include Rails.application.routes.url_helpers
35
+ end
36
+
37
+ class BirdsController < ApplicationController
38
+ def index
39
+ head :ok
40
+ end
41
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: presenter_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Máximo Mussini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-11 00:00:00.000000000 Z
11
+ date: 2017-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -25,19 +25,61 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: pakiderm
28
+ name: railties
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actionmailer
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
32
46
  - !ruby/object:Gem::Version
33
47
  version: '0'
34
- type: :runtime
48
+ type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-given
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
41
83
  description: Presenter helps you expose view models to your views with a declarative
42
84
  approach.
43
85
  email:
@@ -49,19 +91,24 @@ extra_rdoc_files:
49
91
  files:
50
92
  - README.md
51
93
  - lib/presenter_rails.rb
52
- - lib/presenter_rails/presenter.rb
94
+ - lib/presenter_rails/controller.rb
95
+ - lib/presenter_rails/version.rb
96
+ - spec/features/birds_controller_spec.rb
97
+ - spec/features/birds_mailer_spec.rb
98
+ - spec/presenter_rails/controller_spec.rb
99
+ - spec/spec_helper.rb
100
+ - spec/support/rails_app.rb
53
101
  homepage: https://github.com/ElMassimo/presenter_rails
54
102
  licenses:
55
103
  - MIT
56
104
  metadata: {}
57
105
  post_install_message:
58
- rdoc_options:
59
- - "--charset=UTF-8"
106
+ rdoc_options: []
60
107
  require_paths:
61
108
  - lib
62
109
  required_ruby_version: !ruby/object:Gem::Requirement
63
110
  requirements:
64
- - - ">="
111
+ - - "~>"
65
112
  - !ruby/object:Gem::Version
66
113
  version: '2.0'
67
114
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -71,9 +118,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
118
  version: '0'
72
119
  requirements: []
73
120
  rubyforge_project:
74
- rubygems_version: 2.2.2
121
+ rubygems_version: 2.5.1
75
122
  signing_key:
76
123
  specification_version: 4
77
124
  summary: ViewModels had a baby with helper_method
78
- test_files: []
79
- has_rdoc:
125
+ test_files:
126
+ - spec/features/birds_controller_spec.rb
127
+ - spec/features/birds_mailer_spec.rb
128
+ - spec/presenter_rails/controller_spec.rb
129
+ - spec/spec_helper.rb
130
+ - spec/support/rails_app.rb
@@ -1,55 +0,0 @@
1
- require 'pakiderm'
2
-
3
- module PresenterRails
4
- module Presenter
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- extend Pakiderm
9
- end
10
-
11
- module ClassMethods
12
-
13
- # Can take a list of presenter methods, or a name and block for a presenter method
14
- def present(*methods, &block)
15
- presenter_methods = methods.map {|name| Presenter.method_name_for(name) }
16
- define_presenter_method!(presenter_methods, &block) if block_given?
17
-
18
- expose_presenter *methods
19
- memoize *presenter_methods, assignable: true
20
- end
21
-
22
- # Exposes a presenter method to the view for each provided name
23
- def expose_presenter(*method_names)
24
- presenters_module = Module.new do
25
- method_names.each do |name|
26
- module_eval <<-ruby_eval, __FILE__, __LINE__ + 1
27
- def #{name}
28
- controller.send('#{Presenter.method_name_for(name)}')
29
- end
30
- ruby_eval
31
- end
32
- end
33
- helper presenters_module
34
- end
35
-
36
- private
37
-
38
- # Defines a private presenter method that invokes the provided block
39
- def define_presenter_method!(methods, &block)
40
- if methods.size != 1
41
- Kernel.abort "[ERROR] You are providing a block for the `#{methods.join(', ')}` methods, " \
42
- "but you can only provide a block for a single presenter at a time.\n #{caller.second}"
43
- end
44
- presenter_method = methods.first
45
- define_method presenter_method, &block
46
- private presenter_method
47
- end
48
- end
49
-
50
- # Name of the presenter methods
51
- def self.method_name_for(name)
52
- "#{name}_presenter"
53
- end
54
- end
55
- end