actionpresenter 0.1.0 → 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
  SHA1:
3
- metadata.gz: 0d8b47e1b57874329395b699e63f2e0f98f45a28
4
- data.tar.gz: 304807c82fee9a63015e74b9cf49f78c18e50d14
3
+ metadata.gz: 9d45c11dc2f8df677e12c23a12353e25c03c8a7d
4
+ data.tar.gz: 78851cae029d7bf731fb22404e83260f67d32008
5
5
  SHA512:
6
- metadata.gz: 22ec512986082c67bf4a8370f0f158462716b72b89d3198e69c815f3cee4009a765859af99568f56d4a8115b40807d6a5ab853c0615103171cde543e3aa88760
7
- data.tar.gz: 5825f13f5edf01adcd9c270ce07c78b431f2c8249b0581d770e9922a2898efe14481645bd24741d7a4793b3e1a6a2c8786f36e900ee36e1ee76bffe2f0d300dc
6
+ metadata.gz: 229ae78380434fd9be7f881a04794ea631915713aa444413b6a9be9fe7862e1e776a3cb7836af25da9ef7c03085cce00cbe5b9073e98642d53411ef0c9df2640
7
+ data.tar.gz: 3b936e60e9591507261277abfeb93080c1bb1db70291016182b9482690ee438f3d699e7e0cccaa322260070a6be8ffcd9a16104c1d5ec2b8ed831011f0c8529f
data/Gemfile CHANGED
@@ -9,4 +9,5 @@ group :development do
9
9
  gem 'jeweler', '~> 2.0.1'
10
10
  gem 'rspec-rails', '~> 3.0'
11
11
  gem 'pry-byebug'
12
+ gem 'faker'
12
13
  end
@@ -41,6 +41,8 @@ GEM
41
41
  thread_safe (~> 0.3, >= 0.3.1)
42
42
  diff-lcs (1.2.5)
43
43
  erubis (2.7.0)
44
+ faker (1.4.3)
45
+ i18n (~> 0.5)
44
46
  faraday (0.9.0)
45
47
  multipart-post (>= 1.2, < 3)
46
48
  git (1.2.8)
@@ -158,6 +160,7 @@ PLATFORMS
158
160
 
159
161
  DEPENDENCIES
160
162
  bundler (~> 1.0)
163
+ faker
161
164
  jeweler (~> 2.0.1)
162
165
  pry-byebug
163
166
  rails (~> 4.0)
@@ -0,0 +1,118 @@
1
+ # ActionPresenter
2
+
3
+ [![Code Climate](https://codeclimate.com/github/tlux/actionpresenter/badges/gpa.svg)](https://codeclimate.com/github/tlux/actionpresenter)
4
+
5
+ A lightweight presenter implementation for Ruby on Rails.
6
+
7
+ ### Requirements
8
+ * Ruby on Rails >= 4
9
+
10
+ ### Installation
11
+ 1. Add this to your Gemfile:
12
+ ```ruby
13
+ gem 'actionpresenter', '0.2.0'
14
+ ```
15
+
16
+ 2. Create the `app/presenters` folder in your project which will be holding your presenter files.
17
+
18
+ ### Usage
19
+ Lets say we have a model named `User`.
20
+
21
+ ```ruby
22
+ class User < ActiveRecord::Base
23
+ # Model with columns first_name and last_name
24
+ # ...
25
+ def name
26
+ "#{first_name} #{last_name}".strip
27
+ end
28
+ end
29
+ ```
30
+
31
+ ##### Creating a Presenter
32
+ In our views we always want to display the name of a specific user with a link to his profile. The simplest way of providing similar behavior to all our views is to create a `UserPresenter` in `app/presenters/user_presenter.rb`.
33
+
34
+ ```ruby
35
+ class UserPresenter < ActionPresenter::Base
36
+ presents :user
37
+
38
+ def name(linked = true)
39
+ h.link_to_if linked, super(), h.user_profile_path(user)
40
+ end
41
+ end
42
+ ```
43
+
44
+ We can use `super` as alternative to `user.name` or `object.name` in this case to get the value from the original `User` instance.
45
+
46
+ All helpers from the view context are available through the `template` accessor (or its short variant `h`).
47
+
48
+ You can also make all view helpers available to the presenter as if they were mixed into the presenter by including `ActionPresenter::TemplateDelegation`:
49
+
50
+ ```ruby
51
+ class UserPresenter < ActionPresenter::Base
52
+ include ActionPresenter::TemplateDelegation
53
+
54
+ presents :user
55
+
56
+ def name(linked = true)
57
+ link_to_if linked, super(), user_profile_path(user)
58
+ end
59
+ end
60
+ ```
61
+
62
+ ##### Using Presenters in a View
63
+ A presenter can be embedded into a view by using the `present` helper.
64
+ ```erb
65
+ <% present @user do |user| %>
66
+ <strong>Name:</strong>
67
+ <br>
68
+ <%= user.name %>
69
+ <% end %>
70
+ ```
71
+ You can also use reference to any custom presenter:
72
+ ```erb
73
+ <% present @user, with: Admin::UserPresenter do |user| %>
74
+ ...
75
+ <% end %>
76
+ ```
77
+
78
+ Alternatively, you can use:
79
+ ```erb
80
+ <% present [:admin, @user] do |user| %>
81
+ ...
82
+ <% end %>
83
+ ```
84
+
85
+ If you have a collection of objects you can rely on `present_collection` which will wrap each element of the collection with its own presenter:
86
+
87
+ ```erb
88
+ <% present_collection(@users).each do |user| %>
89
+ ...
90
+ <% end %>
91
+ ```
92
+
93
+ The following snippet will search for matching presenter classes within the `Admin` module:
94
+ ```erb
95
+ <% present_collection(@users, scope: :admin).each do |user| %>
96
+ ...
97
+ <% end %>
98
+ ```
99
+
100
+ ##### Keeping it DRY
101
+ To share common functionality between different presenters we encourage you to use presenter concerns. Latter should be put into `app/presenters/concerns`. Note: As this folder is not recognized by the default Rails setup, it has to be added manually to `config/application.rb`:
102
+ ```ruby
103
+ config.autoload_paths << Rails.root.join('app', 'presenters', 'concerns').to_s
104
+ ```
105
+
106
+ ### Contributing to ActionPresenter
107
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
108
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
109
+ * Fork the project.
110
+ * Start a feature/bugfix branch.
111
+ * Commit and push until you are happy with your contribution.
112
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
113
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
114
+
115
+ ### Copyright
116
+ Copyright (c) 2015 Tobias Casper. See LICENSE.txt for
117
+ further details.
118
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -2,28 +2,28 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: actionpresenter 0.1.0 ruby lib
5
+ # stub: actionpresenter 0.2.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "actionpresenter"
9
- s.version = "0.1.0"
9
+ s.version = "0.2.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Tobias Casper"]
14
- s.date = "2014-12-02"
14
+ s.date = "2015-01-18"
15
15
  s.description = "A lightweight presenter implementation for Ruby on Rails."
16
16
  s.email = "tobias.casper@gmail.com"
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE.txt",
19
- "README.rdoc"
19
+ "README.md"
20
20
  ]
21
21
  s.files = [
22
22
  ".document",
23
23
  "Gemfile",
24
24
  "Gemfile.lock",
25
25
  "LICENSE.txt",
26
- "README.rdoc",
26
+ "README.md",
27
27
  "Rakefile",
28
28
  "VERSION",
29
29
  "actionpresenter.gemspec",
@@ -33,7 +33,7 @@ Gem::Specification.new do |s|
33
33
  "lib/action_presenter/template_delegation.rb",
34
34
  "lib/actionpresenter.rb",
35
35
  "spec/helpers/helpers_spec.rb",
36
- "spec/presenters/.keep",
36
+ "spec/presenters/base_spec.rb",
37
37
  "spec/spec_helper.rb",
38
38
  "spec/support/faked_view_context.rb",
39
39
  "spec/support/models.rb",
@@ -53,12 +53,14 @@ Gem::Specification.new do |s|
53
53
  s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
54
54
  s.add_development_dependency(%q<rspec-rails>, ["~> 3.0"])
55
55
  s.add_development_dependency(%q<pry-byebug>, [">= 0"])
56
+ s.add_development_dependency(%q<faker>, [">= 0"])
56
57
  else
57
58
  s.add_dependency(%q<rails>, ["~> 4.0"])
58
59
  s.add_dependency(%q<bundler>, ["~> 1.0"])
59
60
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
60
61
  s.add_dependency(%q<rspec-rails>, ["~> 3.0"])
61
62
  s.add_dependency(%q<pry-byebug>, [">= 0"])
63
+ s.add_dependency(%q<faker>, [">= 0"])
62
64
  end
63
65
  else
64
66
  s.add_dependency(%q<rails>, ["~> 4.0"])
@@ -66,6 +68,7 @@ Gem::Specification.new do |s|
66
68
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
67
69
  s.add_dependency(%q<rspec-rails>, ["~> 3.0"])
68
70
  s.add_dependency(%q<pry-byebug>, [">= 0"])
71
+ s.add_dependency(%q<faker>, [">= 0"])
69
72
  end
70
73
  end
71
74
 
@@ -9,6 +9,7 @@ module ActionPresenter
9
9
  end
10
10
  end
11
11
 
12
- Dir[ActionPresenter.root.join('lib', 'action_presenter', '*.rb')].each do |filename|
12
+ required_files = Dir[ActionPresenter.root.join('lib/action_presenter/*.rb')]
13
+ required_files.each do |filename|
13
14
  require filename
14
15
  end
@@ -6,37 +6,41 @@ class ActionPresenter::Base
6
6
  end
7
7
 
8
8
  attr_reader :object, :options
9
- delegate :present, to: :h
10
- protected :present, :options
9
+ delegate :present, :present_collection, to: :h
10
+ protected :present, :present_collection, :options
11
11
 
12
12
  def self.presents(name)
13
13
  define_method name do
14
- self.object
14
+ object
15
15
  end
16
16
  end
17
17
 
18
18
  def self.delegate_presented(name, options = {})
19
- delegate_opts = options.slice(:to, :prefix, :allow_nil).reverse_merge(to: :object)
19
+ delegate_opts = options.slice(:to, :prefix, :allow_nil)
20
+ .reverse_merge(to: :object)
20
21
  delegate name, delegate_opts
21
22
 
22
23
  define_method "#{name}_with_presenter" do
23
- delegated_obj = self.public_send("#{name}_without_presenter")
24
- present delegated_obj, options.except(*delegate_opts.keys) unless delegated_obj.nil?
24
+ object = public_send("#{name}_without_presenter")
25
+ return if object.nil?
26
+ helper_options = options.except(*delegate_opts.keys)
27
+ if object.respond_to?(:each)
28
+ present_collection object, helper_options
29
+ else
30
+ present object, helper_options
31
+ end
25
32
  end
26
33
 
27
34
  alias_method_chain name, :presenter
28
35
  end
29
36
 
30
37
  def inspect
31
- if object.nil?
32
- "#<#{self.class.name}>"
33
- else
34
- "#<#{self.class.name} object: #{object.inspect}>"
35
- end
38
+ object_str = " object: #{object.inspect}" unless object.nil?
39
+ "#<#{self.class.name}#{object_str}>"
36
40
  end
37
41
 
38
42
  def method_missing(name, *args, &block)
39
- if object and object.respond_to?(name, false)
43
+ if object && object.respond_to?(name, false)
40
44
  object.send(name, *args, &block)
41
45
  else
42
46
  super
@@ -1,22 +1,60 @@
1
1
  require 'action_view/base'
2
2
 
3
3
  module ActionPresenter::Helpers
4
+ VALID_OPTIONS = :with, :scope
5
+
4
6
  def present(*args)
5
7
  options = args.extract_options!
6
- scope = Array(args.first)
7
- object = scope.last
8
- presenter_class = options.delete(:with) do
9
- raise ArgumentError, 'Neither object nor presenter class specified' if object.nil?
10
- scoped_class_name = scope.map { |item|
11
- item.is_a?(Symbol) ? item.to_s.camelize : item.class.name
12
- }.join('::')
13
- "#{scoped_class_name}Presenter"
14
- end
15
- presenter_class = presenter_class.to_s.constantize unless presenter_class.is_a?(Class)
16
- presenter = presenter_class.new(self, object, options)
8
+ scoped_object = Array(args.first)
9
+ object = scoped_object.last
10
+ presenter_class = extract_presenter_class(scoped_object, options)
11
+ presenter_opts = options.except(*VALID_OPTIONS)
12
+ presenter = presenter_class.new(self, object, presenter_opts)
17
13
  yield(presenter) if block_given?
18
14
  presenter
19
15
  end
16
+
17
+ def present_collection(collection, options = {}, &block)
18
+ if collection.nil? || !collection.respond_to?(:to_a)
19
+ fail ArgumentError, 'No valid collection specified'
20
+ end
21
+
22
+ collection.to_a.compact.map do |object|
23
+ present(object, options, &block)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def extract_presenter_class(scope_and_object, options)
30
+ scope_and_object = Array(scope_and_object).compact
31
+ object = scope_and_object.last
32
+
33
+ presenter_class = options.fetch(:with) do
34
+ if object.nil?
35
+ fail ArgumentError, 'Neither object nor presenter class specified'
36
+ end
37
+
38
+ if options[:scope]
39
+ fail ArgumentError, 'You cannot use :scope in conjunction with ' \
40
+ 'a scoped object' if scope_and_object.many?
41
+ scope_and_object = [*options[:scope], object]
42
+ end
43
+
44
+ scoped_class_name = scope_and_object.map do |item|
45
+ case item
46
+ when Symbol then item.to_s.camelize
47
+ when Class then item.name
48
+ else item.class.name
49
+ end
50
+ end.join('::')
51
+
52
+ "#{scoped_class_name}Presenter"
53
+ end
54
+
55
+ return presenter_class.to_s.constantize unless presenter_class.is_a?(Class)
56
+ presenter_class
57
+ end
20
58
  end
21
59
 
22
60
  ActionView::Base.send :include, ActionPresenter::Helpers
@@ -16,8 +16,9 @@ module ActionPresenter::TemplateDelegation
16
16
  end
17
17
  end
18
18
 
19
- def respond_to_missing_with_template_delegation?(name, include_private = false)
19
+ def respond_to_missing_with_template_delegation?(name,
20
+ include_private = false)
20
21
  @_template.respond_to?(name, include_private) ||
21
- respond_to_missing_without_template_delegation?(name, include_private)
22
+ respond_to_missing_without_template_delegation?(name, include_private)
22
23
  end
23
24
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActionPresenter::Helpers do
4
- let(:helper) { FakedViewContext.new }
4
+ subject(:helper) { FakedViewContext.new }
5
5
 
6
6
  describe '#present' do
7
7
  it 'is available in view context' do
@@ -9,7 +9,8 @@ describe ActionPresenter::Helpers do
9
9
  end
10
10
 
11
11
  it 'cannot be invoked without arguments' do
12
- expect { helper.present }.to raise_error ArgumentError, 'Neither object nor presenter class specified'
12
+ expect { helper.present }.to raise_error(
13
+ ArgumentError, 'Neither object nor presenter class specified')
13
14
  end
14
15
 
15
16
  it 'initializes a presenter by a given object' do
@@ -18,7 +19,9 @@ describe ActionPresenter::Helpers do
18
19
  end
19
20
 
20
21
  it 'fails initialization if no matching presenter class could be found' do
21
- expect { helper.present(Location.new) }.to raise_error(/uninitialized constant/)
22
+ expect { helper.present(Location.new) }.to(
23
+ raise_error(/uninitialized constant/)
24
+ )
22
25
  end
23
26
 
24
27
  it 'initializes a presenter by a given object and presenter class' do
@@ -36,17 +39,82 @@ describe ActionPresenter::Helpers do
36
39
  expect(presenter).to be_an Admin::PersonPresenter
37
40
  end
38
41
 
39
- it 'initializes a presenter by a given scoped object and presenter class name (ignoring the scope)' do
40
- presenter = helper.present([:unknown, :scope, Person.new], with: Admin::PersonPresenter)
42
+ it 'initializes a presenter by a given scoped object and presenter class ' \
43
+ 'name (ignoring the scope)' do
44
+ presenter = helper.present([:unknown, :scope, Person.new],
45
+ with: Admin::PersonPresenter)
41
46
  expect(presenter).to be_an Admin::PersonPresenter
42
47
  end
43
48
 
44
49
  it 'fails initializing a presenter with an invalidly scoped object' do
45
- expect { helper.present([:unknown, :scope, Person.new]) }.to raise_error(/uninitialized constant/)
50
+ expect { helper.present([:unknown, :scope, Person.new]) }.to(
51
+ raise_error(/uninitialized constant/)
52
+ )
46
53
  end
47
54
 
48
- it 'takes a block and yields the just initialized presenter instance as argument' do
49
- expect { |block| helper.present(Person.new, &block) }.to yield_with_args(PersonPresenter)
55
+ it 'takes a block and yields the just initialized presenter instance as ' \
56
+ 'argument' do
57
+ expect { |block| helper.present(Person.new, &block) }.to(
58
+ yield_with_args(PersonPresenter)
59
+ )
60
+ end
61
+
62
+ it 'cannot use :scope together with a scoped object' do
63
+ expect { helper.present([:admin, Person.new], scope: :something) }.to(
64
+ raise_error('You cannot use :scope in conjunction with a scoped object')
65
+ )
66
+ end
67
+ end
68
+
69
+ describe '#present_collection' do
70
+ it 'is available in view context' do
71
+ expect(helper).to respond_to(:present_collection)
72
+ end
73
+
74
+ it 'cannot be invoked without arguments' do
75
+ expect { helper.present_collection }.to raise_error(
76
+ ArgumentError, /wrong number of arguments/)
77
+ end
78
+
79
+ it 'takes a collection as first argument' do
80
+ expect(helper.present_collection([Person.new, Person.new]))
81
+ end
82
+
83
+ it 'does not take objects as first argument that do not respond to to_a' do
84
+ expect { helper.present_collection(Person.new) }.to raise_error(
85
+ ArgumentError, 'No valid collection specified'
86
+ )
87
+ end
88
+
89
+ it 'initializes a collection of presenters by their given objects' do
90
+ items = helper.present_collection [Person.new, Company.new]
91
+
92
+ expect(items.first).to be_a PersonPresenter
93
+ expect(items.last).to be_a CompanyPresenter
94
+ end
95
+
96
+ it 'ignores nil elements in the collection' do
97
+ items = helper.present_collection [Person.new, nil, Company.new]
98
+
99
+ expect(items.count).to eq 2
100
+ end
101
+
102
+ it 'initializes a collection of presenters by their given objects and ' \
103
+ 'presenter class' do
104
+ items = helper.present_collection [Person.new, Person.new],
105
+ with: Admin::PersonPresenter
106
+ expect(
107
+ items.all? { |item| item.is_a?(Admin::PersonPresenter) }
108
+ ).to be true
109
+ end
110
+
111
+ it 'initializes a collection of presenters by their given object and ' \
112
+ 'presenter class name' do
113
+ items = helper.present_collection [Person.new, Person.new],
114
+ with: 'Admin::PersonPresenter'
115
+ expect(
116
+ items.all? { |item| item.is_a?(Admin::PersonPresenter) }
117
+ ).to be true
50
118
  end
51
119
  end
52
120
  end
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActionPresenter::Base do
4
+ let(:view_context) { FakedViewContext.new }
5
+
6
+ it 'cannot be initialized without argument' do
7
+ expect { ActionPresenter::Base.new }.to raise_error(
8
+ ArgumentError, /wrong number of arguments/
9
+ )
10
+ end
11
+
12
+ describe '.presents' do
13
+ let(:presenter_class) { Class.new(ActionPresenter::Base) }
14
+
15
+ before :each do
16
+ presenter_class.presents :person
17
+ end
18
+
19
+ it 'creates an alias for #object on a particular presenter class' do
20
+ expect(presenter_class.instance_methods).to include :person
21
+ end
22
+
23
+ it 'returns the original object' do
24
+ person = Person.new
25
+ presenter = presenter_class.new(view_context, person)
26
+ expect(presenter.person).to eq person
27
+ end
28
+ end
29
+
30
+ describe '.delegate_presented' do
31
+ let(:person) { Person.new }
32
+ let!(:presenter_class) { Class.new(ActionPresenter::Base) }
33
+
34
+ before :each do
35
+ presenter_class.presents :person
36
+ end
37
+
38
+ subject(:presenter) { presenter_class.new(view_context, person) }
39
+
40
+ it 'delegates to the original object and wraps it in a presenter' do
41
+ presenter_class.delegate_presented :company
42
+
43
+ expect(presenter.company).to be_a CompanyPresenter
44
+ end
45
+
46
+ it 'delegates to the original collection and wraps each element in its' \
47
+ 'own presenter' do
48
+ presenter_class.delegate_presented :associates
49
+
50
+ expect(presenter.associates).to be_an Array
51
+ expect(presenter.associates.all? { |item|
52
+ item.is_a?(PersonPresenter)
53
+ }).to be true
54
+ end
55
+
56
+ it 'delegates to the original collection and wraps each element in its ' \
57
+ 'own custom defined presenter' do
58
+ presenter_class.delegate_presented :associates, scope: :admin
59
+
60
+ expect(presenter.associates.all? { |item|
61
+ item.is_a?(Admin::PersonPresenter)
62
+ }).to be true
63
+ end
64
+ end
65
+
66
+ describe '#object', '#to_model' do
67
+ let(:person) { Person.new }
68
+ subject(:presenter) { ActionPresenter::Base.new(view_context, person) }
69
+
70
+ it 'includes the original object' do
71
+ expect(presenter.object).to eq person
72
+ end
73
+
74
+ it 'includes the original object (through the #to_model alias)' do
75
+ expect(presenter.to_model).to eq person
76
+ end
77
+ end
78
+
79
+ describe '#template', '#h' do
80
+ subject(:presenter) { ActionPresenter::Base.new(view_context) }
81
+
82
+ it 'is protected' do
83
+ expect(presenter.protected_methods).to include :template
84
+ end
85
+
86
+ it 'is protected (#h alias)' do
87
+ expect(presenter.protected_methods).to include :h
88
+ end
89
+
90
+ it 'returns the view context object' do
91
+ expect(presenter.send(:template)).to eq view_context
92
+ end
93
+
94
+ it 'returns the view context object (through the #h alias)' do
95
+ expect(presenter.send(:h)).to eq view_context
96
+ end
97
+ end
98
+
99
+ describe '#present' do
100
+ let(:person) { Person.new }
101
+ subject(:presenter) { PersonPresenter.new(view_context, person) }
102
+ let :other_presenter do
103
+ presenter.send(:present, presenter.object.company)
104
+ end
105
+
106
+ it 'is protected' do
107
+ expect(presenter.protected_methods).to include :present
108
+ end
109
+
110
+ it 'returns a presenter object' do
111
+ expect(other_presenter).to be_a CompanyPresenter
112
+ end
113
+
114
+ it 'returns a presenter object with the presented object wrapped inside' do
115
+ expect(other_presenter.object).to eq presenter.object.company
116
+ end
117
+ end
118
+
119
+ describe '#present_collection' do
120
+ let(:presenter) { PersonPresenter.new(view_context, Person.new) }
121
+
122
+ it 'is protected' do
123
+ expect(presenter.protected_methods).to include :present_collection
124
+ end
125
+
126
+ it 'returns a collection of presenter objects' do
127
+ other_presenter_collection = presenter.send :present_collection,
128
+ presenter.object.associates
129
+
130
+ expect(other_presenter_collection.all? { |item|
131
+ item.is_a?(PersonPresenter)
132
+ }).to be true
133
+ end
134
+ end
135
+
136
+ context 'dynamic delegation' do
137
+ let(:person) { Person.new }
138
+ let! :presenter_class do
139
+ Class.new(ActionPresenter::Base) do
140
+ presents :person
141
+ end
142
+ end
143
+ subject(:presenter) { presenter_class.new(view_context, person) }
144
+
145
+ it 'delegates to original object unless method has been overridden in ' \
146
+ 'presenter' do
147
+ expect(presenter.name).to eq person.name
148
+ end
149
+
150
+ it 'allows using super to delegate to methods of the original object' do
151
+ presenter_class.class_eval do
152
+ def name
153
+ "Name: #{super}"
154
+ end
155
+ end
156
+
157
+ expect(presenter.name).to eq "Name: #{person.name}"
158
+ end
159
+ end
160
+
161
+ context 'template delegation' do
162
+ before :each do
163
+ allow(view_context).to receive(:content_tag) do |tag, content, opts = {}|
164
+ "<#{tag}>#{content}</#{tag}>"
165
+ end
166
+ end
167
+
168
+ let! :presenter_class do
169
+ Class.new(ActionPresenter::Base) do
170
+ include ActionPresenter::TemplateDelegation
171
+ end
172
+ end
173
+
174
+ let(:presenter) { presenter_class.new(view_context, Person.new) }
175
+
176
+ it 'delegates unknown presenter methods to view context' do
177
+ presenter_class.class_eval do
178
+ def name
179
+ content_tag :strong, super
180
+ end
181
+ end
182
+
183
+ expect { presenter.name }.not_to raise_error
184
+ end
185
+ end
186
+ end
@@ -3,7 +3,13 @@ require 'bundler/setup'
3
3
  require 'pry-byebug'
4
4
  require 'active_model'
5
5
  require 'action_presenter'
6
+ require 'faker'
6
7
 
7
8
  require 'support/faked_view_context'
8
9
  require 'support/models'
9
- require 'support/presenters'
10
+ require 'support/presenters'
11
+
12
+ RSpec.configure do |config|
13
+ config.filter_run focus: true
14
+ config.run_all_when_everything_filtered = true
15
+ end
@@ -1,7 +1,32 @@
1
1
  class Person
2
2
  include ActiveModel::Model
3
+
4
+ def name
5
+ @name ||= Faker::Name.name
6
+ end
7
+
8
+ def address
9
+ @address ||= Faker::Address.street_address
10
+ end
11
+
12
+ def company
13
+ @company ||= Company.new
14
+ end
15
+
16
+ def associates
17
+ @associates ||= 3.times.collect { Person.new }
18
+ end
3
19
  end
4
20
 
5
21
  class Location
6
22
  include ActiveModel::Model
7
- end
23
+ end
24
+
25
+ class Company
26
+ include ActiveModel::Model
27
+
28
+ def name
29
+ @name ||= Faker::Company.name
30
+ end
31
+ end
32
+
@@ -1,6 +1,12 @@
1
1
  class PersonPresenter < ActionPresenter::Base
2
2
  end
3
3
 
4
+ class CompanyPresenter < ActionPresenter::Base
5
+ end
6
+
7
+ class CompanyCollectionPresenter < ActionPresenter::Base
8
+ end
9
+
4
10
  module Admin
5
11
  class PersonPresenter < ActionPresenter::Base
6
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionpresenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Casper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-02 00:00:00.000000000 Z
11
+ date: 2015-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -80,19 +80,33 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faker
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'
83
97
  description: A lightweight presenter implementation for Ruby on Rails.
84
98
  email: tobias.casper@gmail.com
85
99
  executables: []
86
100
  extensions: []
87
101
  extra_rdoc_files:
88
102
  - LICENSE.txt
89
- - README.rdoc
103
+ - README.md
90
104
  files:
91
105
  - ".document"
92
106
  - Gemfile
93
107
  - Gemfile.lock
94
108
  - LICENSE.txt
95
- - README.rdoc
109
+ - README.md
96
110
  - Rakefile
97
111
  - VERSION
98
112
  - actionpresenter.gemspec
@@ -102,7 +116,7 @@ files:
102
116
  - lib/action_presenter/template_delegation.rb
103
117
  - lib/actionpresenter.rb
104
118
  - spec/helpers/helpers_spec.rb
105
- - spec/presenters/.keep
119
+ - spec/presenters/base_spec.rb
106
120
  - spec/spec_helper.rb
107
121
  - spec/support/faked_view_context.rb
108
122
  - spec/support/models.rb
@@ -1,21 +0,0 @@
1
- = ActionPresenter
2
-
3
- {<img src="https://codeclimate.com/github/tlux/actionpresenter/badges/gpa.svg" />}[https://codeclimate.com/github/tlux/actionpresenter]
4
-
5
- A lightweight presenter implementation for Ruby on Rails.
6
-
7
- == Contributing to actionpresenter
8
-
9
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
10
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
11
- * Fork the project.
12
- * Start a feature/bugfix branch.
13
- * Commit and push until you are happy with your contribution.
14
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
15
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
16
-
17
- == Copyright
18
-
19
- Copyright (c) 2014 Tobias Casper. See LICENSE.txt for
20
- further details.
21
-
File without changes