strong_presenter 0.2.1 → 0.2.2
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.
- data/CHANGELOG.md +4 -0
- data/lib/strong_presenter.rb +2 -2
- data/lib/strong_presenter/associable.rb +1 -2
- data/lib/strong_presenter/collection_presenter.rb +1 -2
- data/lib/strong_presenter/presenter.rb +1 -80
- data/lib/strong_presenter/version.rb +1 -1
- data/spec/strong_presenter/associable_spec.rb +1 -7
- data/strong_presenter.gemspec +2 -0
- metadata +69 -99
- checksums.yaml +0 -7
- data/lib/strong_presenter/permissible.rb +0 -50
- data/lib/strong_presenter/permissions.rb +0 -141
- data/spec/strong_presenter/permissible_spec.rb +0 -24
- data/spec/strong_presenter/permissions_spec.rb +0 -202
data/CHANGELOG.md
CHANGED
data/lib/strong_presenter.rb
CHANGED
@@ -11,13 +11,13 @@ require 'active_support/core_ext/hash/keys'
|
|
11
11
|
require 'active_support/core_ext/hash/reverse_merge'
|
12
12
|
require 'active_support/core_ext/name_error'
|
13
13
|
|
14
|
+
require "strong_attributes"
|
15
|
+
|
14
16
|
require "strong_presenter/version"
|
15
17
|
require "strong_presenter/view_helpers"
|
16
18
|
require "strong_presenter/helper_proxy"
|
17
19
|
require "strong_presenter/view_context"
|
18
20
|
require "strong_presenter/delegation"
|
19
|
-
require "strong_presenter/permissions"
|
20
|
-
require "strong_presenter/permissible"
|
21
21
|
require "strong_presenter/associable"
|
22
22
|
require "strong_presenter/inferrer"
|
23
23
|
require "strong_presenter/presenter"
|
@@ -3,7 +3,7 @@ module StrongPresenter
|
|
3
3
|
# Methods for defining presenter associations
|
4
4
|
module Associable
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
include
|
6
|
+
include StrongAttributes::Permissible
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
# Automatically wraps multiple associations.
|
@@ -57,7 +57,6 @@ module StrongPresenter
|
|
57
57
|
end
|
58
58
|
|
59
59
|
# Permits given attributes, with propagation to associations.
|
60
|
-
# @param (see StrongPresenter::Permissible#permit!)
|
61
60
|
def permit! *attribute_paths
|
62
61
|
super
|
63
62
|
attribute_paths.each do |path| # propagate permit to associations
|
@@ -2,8 +2,8 @@ module StrongPresenter
|
|
2
2
|
class CollectionPresenter
|
3
3
|
include Enumerable
|
4
4
|
include StrongPresenter::ViewHelpers
|
5
|
-
include StrongPresenter::Permissible
|
6
5
|
include StrongPresenter::Delegation
|
6
|
+
include StrongAttributes::Permissible
|
7
7
|
|
8
8
|
array_methods = Array.instance_methods - Object.instance_methods
|
9
9
|
delegate :==, :as_json, *array_methods, to: :collection
|
@@ -23,7 +23,6 @@ module StrongPresenter
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# Permits given attributes, with propagation to collection items.
|
26
|
-
# @param (see StrongPresenter::Permissible#permit!)
|
27
26
|
def permit! *attribute_paths
|
28
27
|
super
|
29
28
|
@collection.each { |presenter| presenter.permit! *attribute_paths } unless @collection.nil?
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module StrongPresenter
|
2
2
|
class Presenter
|
3
3
|
include StrongPresenter::ViewHelpers
|
4
|
-
include StrongPresenter::Permissible
|
5
4
|
include StrongPresenter::Associable
|
6
5
|
include StrongPresenter::Delegation
|
6
|
+
include StrongAttributes::Displayable
|
7
7
|
|
8
8
|
include ActiveModel::Serialization
|
9
9
|
include ActiveModel::Serializers::JSON
|
@@ -25,85 +25,6 @@ module StrongPresenter
|
|
25
25
|
yield self if block_given?
|
26
26
|
end
|
27
27
|
|
28
|
-
# Performs mass presentation - if it is allowed, subject to `permit`. To permit all without checking, call `permit!` first.
|
29
|
-
#
|
30
|
-
# Presents and returns the result of each field in the argument list. If a block is given, then each result
|
31
|
-
# is passed to the block. Each field is presented by calling the method on the presenter.
|
32
|
-
#
|
33
|
-
# user_presenter.presents :username, :email # returns [user_presenter.username, user_presenter.email]
|
34
|
-
#
|
35
|
-
# Or with two arguments, the name of the field is passed first:
|
36
|
-
#
|
37
|
-
# <ul>
|
38
|
-
# <% user_presenter.presents :username, :email, :address do |field, value| %>
|
39
|
-
# <li><%= field.capitalize %>: <% value %></li>
|
40
|
-
# <% end %>
|
41
|
-
# </ul>
|
42
|
-
#
|
43
|
-
# If only the presented value is desired, use `each`:
|
44
|
-
#
|
45
|
-
# <% user_presenter.presents(:username, :email).each do |value| %>
|
46
|
-
# <td><%= value %></td>
|
47
|
-
# <% end %>
|
48
|
-
#
|
49
|
-
# A field can have arguments in an array:
|
50
|
-
#
|
51
|
-
# user_presenter.presents :username, [:notifications, :unread] # returns [user_presenter.username, user_presenter.notifications(:unread)]
|
52
|
-
#
|
53
|
-
# Notice that this interface allows you to concisely put authorization logic in the controller, with a dumb view layer:
|
54
|
-
#
|
55
|
-
# # app/controllers/users_controller.rb
|
56
|
-
# class UsersController < ApplicationController
|
57
|
-
# def visible_params
|
58
|
-
# @visible_params ||= begin
|
59
|
-
# field = [:username]
|
60
|
-
# field << :email if can? :read_email, @user
|
61
|
-
# field << :edit_link if can? :edit, @user
|
62
|
-
# end
|
63
|
-
# end
|
64
|
-
# def show
|
65
|
-
# @users_presenter = UserPresenter.wrap_each(User.all).permit!
|
66
|
-
# end
|
67
|
-
# end
|
68
|
-
#
|
69
|
-
# # app/views/users/show.html.erb
|
70
|
-
# <table>
|
71
|
-
# <tr>
|
72
|
-
# <% visible_params.each do |field| %>
|
73
|
-
# <th><%= field %></th>
|
74
|
-
# <% end %>
|
75
|
-
# </tr>
|
76
|
-
# <% @users_presenter.each do |user_presenter| %>
|
77
|
-
# <tr>
|
78
|
-
# <% user_presenter.presents(*visible_params).each do |value| %>
|
79
|
-
# <td><%= value %></td>
|
80
|
-
# <% end %>
|
81
|
-
# </tr>
|
82
|
-
# <% end %>
|
83
|
-
# </table>
|
84
|
-
#
|
85
|
-
def presents *attributes
|
86
|
-
select_permitted(*attributes).map do |args|
|
87
|
-
args = Array(args)
|
88
|
-
obj = self # drill into associations
|
89
|
-
while (args.size > 1) && obj.class.respond_to?(:presenter_associations, true) && obj.class.send(:presenter_associations).include?(args[0]) do
|
90
|
-
obj = obj.public_send args.slice!(0)
|
91
|
-
end
|
92
|
-
value = obj.public_send *args # call final method with args
|
93
|
-
yield args[0], value if block_given?
|
94
|
-
value
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Same as presents, but for a single attribute. The differences are:
|
99
|
-
# - the return value is not in an Array
|
100
|
-
# - passes the value only (without attribute key as the 1st argument) to a block
|
101
|
-
def present field
|
102
|
-
presents field do |key, value|
|
103
|
-
yield value if block_given?
|
104
|
-
end.first
|
105
|
-
end
|
106
|
-
|
107
28
|
delegate :to_s
|
108
29
|
|
109
30
|
# In case object is nil
|
@@ -37,7 +37,7 @@ module StrongPresenter
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'presents nested assocations' do
|
40
|
-
@product_presenter.permit!
|
40
|
+
@product_presenter.permit! [:manufacturer, :source, :name]
|
41
41
|
expect(@product_presenter.present [:manufacturer, :source, :name]).to eq "Presented Factory"
|
42
42
|
end
|
43
43
|
|
@@ -273,12 +273,6 @@ module StrongPresenter
|
|
273
273
|
expect(@product_presenter.subproducts[0].presents(:name, :price, :description)).to eq ["Sub A", 8]
|
274
274
|
end
|
275
275
|
|
276
|
-
it 'propagates double wildcard' do
|
277
|
-
@product_presenter.subproducts[0].permit! :price
|
278
|
-
@product_presenter.permit! :**
|
279
|
-
expect(@product_presenter.subproducts[0].presents(:name, "price", :description)).to eq ["Sub A", 8, "Small"]
|
280
|
-
end
|
281
|
-
|
282
276
|
it 'does not propagate single wildcard' do
|
283
277
|
@product_presenter.subproducts[0].permit! :price
|
284
278
|
@product_presenter.permit! :*
|
data/strong_presenter.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.description = %q{strong_presenter adds a layer of presentation logic to your application, and gives you strong_parameters-like logic to determine which attributes are visible}
|
12
12
|
gem.summary = %q{strong_presenter adds a layer of presentation logic to your application, and gives you strong_parameters-like logic to determine which attributes are visible}
|
13
13
|
gem.homepage = "https://github.com/ronalchn/strong_presenter"
|
14
|
+
gem.license = "MPLv2"
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -21,6 +22,7 @@ Gem::Specification.new do |gem|
|
|
21
22
|
gem.add_dependency 'actionpack', '>= 3.0'
|
22
23
|
gem.add_dependency 'request_store', '~> 1.0.3'
|
23
24
|
gem.add_dependency 'activemodel', '>= 3.0'
|
25
|
+
gem.add_dependency 'strong_attributes', '~> 0.0.2'
|
24
26
|
|
25
27
|
gem.add_development_dependency 'ammeter'
|
26
28
|
gem.add_development_dependency 'rake', '>= 0.9.2'
|
metadata
CHANGED
@@ -1,197 +1,170 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Ronald Chan
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2013-
|
12
|
+
date: 2013-11-07 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: activesupport
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirement: &16202200 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
16
18
|
requirements:
|
17
|
-
- - '>='
|
19
|
+
- - ! '>='
|
18
20
|
- !ruby/object:Gem::Version
|
19
21
|
version: '3.0'
|
20
22
|
type: :runtime
|
21
23
|
prerelease: false
|
22
|
-
version_requirements:
|
23
|
-
requirements:
|
24
|
-
- - '>='
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '3.0'
|
24
|
+
version_requirements: *16202200
|
27
25
|
- !ruby/object:Gem::Dependency
|
28
26
|
name: actionpack
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
27
|
+
requirement: &16200920 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
30
29
|
requirements:
|
31
|
-
- - '>='
|
30
|
+
- - ! '>='
|
32
31
|
- !ruby/object:Gem::Version
|
33
32
|
version: '3.0'
|
34
33
|
type: :runtime
|
35
34
|
prerelease: false
|
36
|
-
version_requirements:
|
37
|
-
requirements:
|
38
|
-
- - '>='
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '3.0'
|
35
|
+
version_requirements: *16200920
|
41
36
|
- !ruby/object:Gem::Dependency
|
42
37
|
name: request_store
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirement: &16199520 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
44
40
|
requirements:
|
45
41
|
- - ~>
|
46
42
|
- !ruby/object:Gem::Version
|
47
43
|
version: 1.0.3
|
48
44
|
type: :runtime
|
49
45
|
prerelease: false
|
50
|
-
version_requirements:
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 1.0.3
|
46
|
+
version_requirements: *16199520
|
55
47
|
- !ruby/object:Gem::Dependency
|
56
48
|
name: activemodel
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirement: &16197540 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
58
51
|
requirements:
|
59
|
-
- - '>='
|
52
|
+
- - ! '>='
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '3.0'
|
62
55
|
type: :runtime
|
63
56
|
prerelease: false
|
64
|
-
version_requirements:
|
57
|
+
version_requirements: *16197540
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: strong_attributes
|
60
|
+
requirement: &16195400 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
65
62
|
requirements:
|
66
|
-
- -
|
63
|
+
- - ~>
|
67
64
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
65
|
+
version: 0.0.2
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *16195400
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: ammeter
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirement: &16219680 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
72
73
|
requirements:
|
73
|
-
- - '>='
|
74
|
+
- - ! '>='
|
74
75
|
- !ruby/object:Gem::Version
|
75
76
|
version: '0'
|
76
77
|
type: :development
|
77
78
|
prerelease: false
|
78
|
-
version_requirements:
|
79
|
-
requirements:
|
80
|
-
- - '>='
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
79
|
+
version_requirements: *16219680
|
83
80
|
- !ruby/object:Gem::Dependency
|
84
81
|
name: rake
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
82
|
+
requirement: &16218340 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
86
84
|
requirements:
|
87
|
-
- - '>='
|
85
|
+
- - ! '>='
|
88
86
|
- !ruby/object:Gem::Version
|
89
87
|
version: 0.9.2
|
90
88
|
type: :development
|
91
89
|
prerelease: false
|
92
|
-
version_requirements:
|
93
|
-
requirements:
|
94
|
-
- - '>='
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 0.9.2
|
90
|
+
version_requirements: *16218340
|
97
91
|
- !ruby/object:Gem::Dependency
|
98
92
|
name: rspec
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirement: &16216400 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
100
95
|
requirements:
|
101
96
|
- - ~>
|
102
97
|
- !ruby/object:Gem::Version
|
103
98
|
version: '2.12'
|
104
99
|
type: :development
|
105
100
|
prerelease: false
|
106
|
-
version_requirements:
|
107
|
-
requirements:
|
108
|
-
- - ~>
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '2.12'
|
101
|
+
version_requirements: *16216400
|
111
102
|
- !ruby/object:Gem::Dependency
|
112
103
|
name: rspec-mocks
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirement: &16214640 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
114
106
|
requirements:
|
115
|
-
- - '>='
|
107
|
+
- - ! '>='
|
116
108
|
- !ruby/object:Gem::Version
|
117
109
|
version: 2.12.1
|
118
110
|
type: :development
|
119
111
|
prerelease: false
|
120
|
-
version_requirements:
|
121
|
-
requirements:
|
122
|
-
- - '>='
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 2.12.1
|
112
|
+
version_requirements: *16214640
|
125
113
|
- !ruby/object:Gem::Dependency
|
126
114
|
name: rspec-rails
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirement: &16213900 !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
128
117
|
requirements:
|
129
118
|
- - ~>
|
130
119
|
- !ruby/object:Gem::Version
|
131
120
|
version: '2.12'
|
132
121
|
type: :development
|
133
122
|
prerelease: false
|
134
|
-
version_requirements:
|
135
|
-
requirements:
|
136
|
-
- - ~>
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '2.12'
|
123
|
+
version_requirements: *16213900
|
139
124
|
- !ruby/object:Gem::Dependency
|
140
125
|
name: minitest-rails
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirement: &16237840 !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
142
128
|
requirements:
|
143
129
|
- - ~>
|
144
130
|
- !ruby/object:Gem::Version
|
145
131
|
version: '0.2'
|
146
132
|
type: :development
|
147
133
|
prerelease: false
|
148
|
-
version_requirements:
|
149
|
-
requirements:
|
150
|
-
- - ~>
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0.2'
|
134
|
+
version_requirements: *16237840
|
153
135
|
- !ruby/object:Gem::Dependency
|
154
136
|
name: capybara
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
137
|
+
requirement: &16236820 !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
156
139
|
requirements:
|
157
|
-
- - '>='
|
140
|
+
- - ! '>='
|
158
141
|
- !ruby/object:Gem::Version
|
159
142
|
version: '0'
|
160
143
|
type: :development
|
161
144
|
prerelease: false
|
162
|
-
version_requirements:
|
163
|
-
requirements:
|
164
|
-
- - '>='
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0'
|
145
|
+
version_requirements: *16236820
|
167
146
|
- !ruby/object:Gem::Dependency
|
168
147
|
name: active_model_serializers
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirement: &16235320 !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
170
150
|
requirements:
|
171
|
-
- - '>='
|
151
|
+
- - ! '>='
|
172
152
|
- !ruby/object:Gem::Version
|
173
153
|
version: '0'
|
174
154
|
type: :development
|
175
155
|
prerelease: false
|
176
|
-
version_requirements:
|
177
|
-
requirements:
|
178
|
-
- - '>='
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: '0'
|
156
|
+
version_requirements: *16235320
|
181
157
|
- !ruby/object:Gem::Dependency
|
182
158
|
name: activerecord
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
159
|
+
requirement: &16234500 !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
184
161
|
requirements:
|
185
|
-
- - '>='
|
162
|
+
- - ! '>='
|
186
163
|
- !ruby/object:Gem::Version
|
187
164
|
version: '3.0'
|
188
165
|
type: :development
|
189
166
|
prerelease: false
|
190
|
-
version_requirements:
|
191
|
-
requirements:
|
192
|
-
- - '>='
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '3.0'
|
167
|
+
version_requirements: *16234500
|
195
168
|
description: strong_presenter adds a layer of presentation logic to your application,
|
196
169
|
and gives you strong_parameters-like logic to determine which attributes are visible
|
197
170
|
email:
|
@@ -233,8 +206,6 @@ files:
|
|
233
206
|
- lib/strong_presenter/factory.rb
|
234
207
|
- lib/strong_presenter/helper_proxy.rb
|
235
208
|
- lib/strong_presenter/inferrer.rb
|
236
|
-
- lib/strong_presenter/permissible.rb
|
237
|
-
- lib/strong_presenter/permissions.rb
|
238
209
|
- lib/strong_presenter/presenter.rb
|
239
210
|
- lib/strong_presenter/presenter_association.rb
|
240
211
|
- lib/strong_presenter/presenter_helper_constructor.rb
|
@@ -323,8 +294,6 @@ files:
|
|
323
294
|
- spec/strong_presenter/controller_additions_spec.rb
|
324
295
|
- spec/strong_presenter/delegation_spec.rb
|
325
296
|
- spec/strong_presenter/helper_proxy_spec.rb
|
326
|
-
- spec/strong_presenter/permissible_spec.rb
|
327
|
-
- spec/strong_presenter/permissions_spec.rb
|
328
297
|
- spec/strong_presenter/presenter_spec.rb
|
329
298
|
- spec/strong_presenter/simplecov_spec.rb
|
330
299
|
- spec/strong_presenter/view_context/build_strategy_spec.rb
|
@@ -337,27 +306,29 @@ files:
|
|
337
306
|
- spec/support/shared_examples/view_helpers.rb
|
338
307
|
- strong_presenter.gemspec
|
339
308
|
homepage: https://github.com/ronalchn/strong_presenter
|
340
|
-
licenses:
|
341
|
-
|
309
|
+
licenses:
|
310
|
+
- MPLv2
|
342
311
|
post_install_message:
|
343
312
|
rdoc_options: []
|
344
313
|
require_paths:
|
345
314
|
- lib
|
346
315
|
required_ruby_version: !ruby/object:Gem::Requirement
|
316
|
+
none: false
|
347
317
|
requirements:
|
348
|
-
- - '>='
|
318
|
+
- - ! '>='
|
349
319
|
- !ruby/object:Gem::Version
|
350
320
|
version: '0'
|
351
321
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
322
|
+
none: false
|
352
323
|
requirements:
|
353
|
-
- - '>='
|
324
|
+
- - ! '>='
|
354
325
|
- !ruby/object:Gem::Version
|
355
326
|
version: '0'
|
356
327
|
requirements: []
|
357
328
|
rubyforge_project:
|
358
|
-
rubygems_version:
|
329
|
+
rubygems_version: 1.8.17
|
359
330
|
signing_key:
|
360
|
-
specification_version:
|
331
|
+
specification_version: 3
|
361
332
|
summary: strong_presenter adds a layer of presentation logic to your application,
|
362
333
|
and gives you strong_parameters-like logic to determine which attributes are visible
|
363
334
|
test_files:
|
@@ -436,8 +407,6 @@ test_files:
|
|
436
407
|
- spec/strong_presenter/controller_additions_spec.rb
|
437
408
|
- spec/strong_presenter/delegation_spec.rb
|
438
409
|
- spec/strong_presenter/helper_proxy_spec.rb
|
439
|
-
- spec/strong_presenter/permissible_spec.rb
|
440
|
-
- spec/strong_presenter/permissions_spec.rb
|
441
410
|
- spec/strong_presenter/presenter_spec.rb
|
442
411
|
- spec/strong_presenter/simplecov_spec.rb
|
443
412
|
- spec/strong_presenter/view_context/build_strategy_spec.rb
|
@@ -448,3 +417,4 @@ test_files:
|
|
448
417
|
- spec/support/models.rb
|
449
418
|
- spec/support/schema.rb
|
450
419
|
- spec/support/shared_examples/view_helpers.rb
|
420
|
+
has_rdoc:
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: 6444649230265391d72546b3298782db1383abc4
|
4
|
-
data.tar.gz: 8ed272da88fb418034afe94c7fa8b20ee52139f5
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: 4a5aef2eea1880590b28c89643e7900df4a19862bbcb9da51077f17dae5b476fb2dec9b1c220e74b1a22ae1f011cb0911b1aec626dee12c91f881e1252316715
|
7
|
-
data.tar.gz: eae1b941dcd897be3ff945636f327d0b2832541df05d6c93c10f461d7bdea334a199313ec54f2c9ef5ececda6d4f90e827e06b8d561a4982dc393480f5755a1e
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module StrongPresenter
|
2
|
-
module Permissible
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
# Permits given attributes. May be invoked multiple times.
|
6
|
-
#
|
7
|
-
# @example Each argument represents a single attribute:
|
8
|
-
# ArticlePresenter.new(@article).permit(:heading, :article)
|
9
|
-
#
|
10
|
-
# @example Attribute paths can be specified using symbol arrays. If an author name is normally accessed using @article.author.name:
|
11
|
-
# ArticlePresenter.new(@article).permit([:author, :name])
|
12
|
-
#
|
13
|
-
# @param [[Symbols*]*] attribute_paths
|
14
|
-
# the attributes to permit. An array of symbols represents an attribute path.
|
15
|
-
# @return [self]
|
16
|
-
def permit! *attribute_paths
|
17
|
-
permitted_attributes.permit *attribute_paths
|
18
|
-
self
|
19
|
-
end
|
20
|
-
|
21
|
-
# Permits all presenter attributes for presents, present & filter methods.
|
22
|
-
def permit_all!
|
23
|
-
permitted_attributes.permit_all!
|
24
|
-
self
|
25
|
-
end
|
26
|
-
|
27
|
-
# Selects the attributes given which have been permitted - an array of attributes
|
28
|
-
# @param [Array<Symbols>*] attribute_paths
|
29
|
-
# the attribute paths to check. The attribute paths may also have arguments.
|
30
|
-
# @return [Array<Array<Symbol>>] attribute (paths)
|
31
|
-
def select_permitted *attribute_paths
|
32
|
-
permitted_attributes.select_permitted *attribute_paths
|
33
|
-
end
|
34
|
-
|
35
|
-
protected
|
36
|
-
def permitted_attributes
|
37
|
-
@permitted_attributes ||= StrongPresenter::Permissions.new
|
38
|
-
end
|
39
|
-
|
40
|
-
# Links presenter to permissions group of given presenter.
|
41
|
-
# @param [Presenter] parent_presenter
|
42
|
-
# @param [Array<Symbol>] relative_path
|
43
|
-
# The prefix prepended before every permission check relative to parent presenter.
|
44
|
-
def link_permissions parent_presenter, relative_path = []
|
45
|
-
@permitted_attributes = StrongPresenter::Permissions.new(parent_presenter.permitted_attributes, relative_path)
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
@@ -1,141 +0,0 @@
|
|
1
|
-
module StrongPresenter
|
2
|
-
# @private
|
3
|
-
#
|
4
|
-
# Storage format:
|
5
|
-
# Each attribute path is stored as an array of objects, usually strings in a Set. For indifferent access,
|
6
|
-
# all symbols are converted to strings.
|
7
|
-
#
|
8
|
-
# Arguments can also be part of permissions control. They are simply additional elements in the attribute path array,
|
9
|
-
# and need not be strings or symbols. Permitting a string or symbol argument automatically permits both.
|
10
|
-
#
|
11
|
-
# When checking if paths with tainted strings/elements are permitted, only exact matches are allowed
|
12
|
-
class Permissions
|
13
|
-
|
14
|
-
def prefix_path
|
15
|
-
@prefix_path || []
|
16
|
-
end
|
17
|
-
|
18
|
-
# Initialize, optionally with link to permitted paths, and the prefix to that (with copy on write semantics)
|
19
|
-
def initialize(permissions = nil, prefix_path = [])
|
20
|
-
unless permissions.nil?
|
21
|
-
@permitted_paths = permissions.permitted_paths
|
22
|
-
@prefix_path = permissions.prefix_path + canonicalize(prefix_path) # copy on write
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Checks whether everything is permitted. Considers :*, which permits all methods
|
27
|
-
# but not association methods to be complete.
|
28
|
-
# @return [Boolean]
|
29
|
-
def complete?
|
30
|
-
permitted? prefix_path + [:*]
|
31
|
-
end
|
32
|
-
|
33
|
-
# Permits wildcard method, but not association methods.
|
34
|
-
# @return self
|
35
|
-
def permit_all!
|
36
|
-
copy_on_write!
|
37
|
-
permitted_paths << [:*]
|
38
|
-
self
|
39
|
-
end
|
40
|
-
|
41
|
-
# Checks if the attribute path is permitted. This is the case if
|
42
|
-
# any array prefix has been permitted.
|
43
|
-
# @param [Object, Array<Object>] prefix_path
|
44
|
-
# @param [Object, Array<Object>] attribute_path
|
45
|
-
# @return [Boolean]
|
46
|
-
def permitted? attribute_path
|
47
|
-
attribute_path = canonicalize(attribute_path)
|
48
|
-
return true if permitted_paths.include? prefix_path + attribute_path # exact match
|
49
|
-
!path_tainted?(attribute_path) and permitted_by_wildcard?(prefix_path + attribute_path) # wildcard match only if not tainted
|
50
|
-
end
|
51
|
-
|
52
|
-
# Selects the attribute paths which are permitted.
|
53
|
-
# @param [Array] prefix_path
|
54
|
-
# namespace in which each of the given attribute paths are in
|
55
|
-
# @param [[Object, Array<Object>]*] *attribute_paths
|
56
|
-
# each attribute path is a symbol or array of symbols
|
57
|
-
# @return [Array<Object, Array<Object>>] array of attribute paths permitted
|
58
|
-
def select_permitted *attribute_paths
|
59
|
-
attribute_paths.select { |attribute_path| permitted?(attribute_path) }
|
60
|
-
end
|
61
|
-
|
62
|
-
# Rejects the attribute paths which are permitted. Opposite of select_permitted.
|
63
|
-
# Returns the attribute paths which are not permitted.
|
64
|
-
#
|
65
|
-
# @param [Array] prefix_path
|
66
|
-
# namespace in which each of the given attribute paths are in
|
67
|
-
# @param [[Object, Array<Object>]*] *attribute_paths
|
68
|
-
# each attribute path is an object(string) or array
|
69
|
-
# @return [Array<Object, Array<Object>>] array of attribute paths remaining
|
70
|
-
def reject_permitted *attribute_paths
|
71
|
-
attribute_paths.reject { |attribute_path| permitted?(attribute_path) }
|
72
|
-
end
|
73
|
-
|
74
|
-
# Permits some attribute paths
|
75
|
-
#
|
76
|
-
# @param [Array] prefix_path
|
77
|
-
# path to prepend to each attribute path
|
78
|
-
# @param [[Object, Array]*] *attribute_paths
|
79
|
-
def permit *attribute_paths
|
80
|
-
copy_on_write!
|
81
|
-
attribute_paths = attribute_paths.map{|path|canonicalize(path).taint} # exact match
|
82
|
-
reject_permitted(*attribute_paths).each do |attribute_path| # don't permit if already permitted
|
83
|
-
permitted_paths << attribute_path # prefix_path = [] because of copy on write
|
84
|
-
end
|
85
|
-
self
|
86
|
-
end
|
87
|
-
|
88
|
-
protected
|
89
|
-
|
90
|
-
def permitted_paths
|
91
|
-
@permitted_paths ||= Set.new
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
# Is this still referencing another objects permissions?
|
96
|
-
def reference?
|
97
|
-
!@prefix_path.nil?
|
98
|
-
end
|
99
|
-
|
100
|
-
# Make a copy if this still references something else, since we are planning on writing soon
|
101
|
-
def copy_on_write!
|
102
|
-
if prefix_path == []
|
103
|
-
@permitted_paths = permitted_paths.dup
|
104
|
-
elsif reference?
|
105
|
-
@permitted_paths, old_set = Set.new, permitted_paths
|
106
|
-
old_set.each do |path|
|
107
|
-
@permitted_paths << path[prefix_path.size...path.size] if path[0...prefix_path.size] == prefix_path
|
108
|
-
end
|
109
|
-
end
|
110
|
-
@prefix_path = nil
|
111
|
-
end
|
112
|
-
|
113
|
-
def path_tainted? attribute_path
|
114
|
-
attribute_path.tainted? or attribute_path.any? { |element| element.tainted? }
|
115
|
-
end
|
116
|
-
|
117
|
-
# Caution: Will mutate path
|
118
|
-
def permitted_by_wildcard? path
|
119
|
-
unless path.empty?
|
120
|
-
path[-1] = :*
|
121
|
-
return true if permitted_paths.include? path
|
122
|
-
end
|
123
|
-
until path.empty?
|
124
|
-
path[-1] = :**
|
125
|
-
return true if permitted_paths.include? path
|
126
|
-
path.pop
|
127
|
-
end
|
128
|
-
false
|
129
|
-
end
|
130
|
-
|
131
|
-
# Converts symbols to strings (except for wildcard symbol)
|
132
|
-
def canonicalize array
|
133
|
-
array = Array(array)
|
134
|
-
canonical_array = array.map{|e|e.is_a?(Symbol) ? e.to_s : e}
|
135
|
-
canonical_array[-1] = array.last if [:*, :**].include? array.last
|
136
|
-
canonical_array.taint if array.tainted?
|
137
|
-
canonical_array
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module StrongPresenter
|
4
|
-
describe Permissible do
|
5
|
-
it "selects permitted attributes" do
|
6
|
-
object = Model.new
|
7
|
-
presenter = Presenter.new(object)
|
8
|
-
|
9
|
-
presenter.permit! :a, :b, :c
|
10
|
-
permitted = presenter.select_permitted :a, :c, :z
|
11
|
-
expect(permitted).to include(:a, :c)
|
12
|
-
expect(permitted).to_not include(:b, :z)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "permits all if permit_all!" do
|
16
|
-
object = Model.new
|
17
|
-
presenter = Presenter.new(object)
|
18
|
-
|
19
|
-
presenter.permit_all!
|
20
|
-
permitted = presenter.select_permitted :a, :b, :c
|
21
|
-
expect(permitted).to include(:a, :b, :c)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,202 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module StrongPresenter
|
4
|
-
describe Permissions do
|
5
|
-
describe "#complete?" do
|
6
|
-
it 'is initially incomplete' do
|
7
|
-
expect(Permissions.new.complete?).to be false
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "#permit_all!" do
|
12
|
-
it 'completes the object' do
|
13
|
-
expect(Permissions.new.permit_all!.complete?).to be true
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "#permit" do
|
18
|
-
it 'permits an attribute' do
|
19
|
-
expect(Permissions.new.permit(:attr).permitted?(:attr)).to be true
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'permits with arrays of symbols' do
|
23
|
-
expect(Permissions.new.permit([:attr, :array]).permitted?([:attr, :array])).to be true
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'permits multiple at once' do
|
27
|
-
permissions = Permissions.new.permit([:another, :array], [:attr, :array], :attr2, :attr3)
|
28
|
-
expect(permissions.permitted?(:attr2)).to be true
|
29
|
-
expect(permissions.permitted?([:attr, :array])).to be true
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'does not mutate arguments' do
|
33
|
-
attrpaths = [[:attr, :path], :s]
|
34
|
-
Permissions.new.permit(attrpathsarg = attrpaths.dup)
|
35
|
-
|
36
|
-
expect(attrpathsarg).to eq attrpaths
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'permits specific path after wildcard' do
|
40
|
-
permissions = Permissions.new.permit([:prefix, :*])
|
41
|
-
expect(permissions.permitted?([:prefix, "specific".taint])).to be false
|
42
|
-
permissions.permit([:prefix, :specific])
|
43
|
-
expect(permissions.permitted?([:prefix, "specific".taint])).to be true
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
describe "#permitted?" do
|
48
|
-
context 'with some attributes permitted' do
|
49
|
-
before(:all) do
|
50
|
-
@permissions = Permissions.new.permit([:another, :array], [:attr, :array], :attr2, :attr3, [:a, :wildcard, :*], [:wild, :**])
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'permits single wildcard' do
|
54
|
-
expect(@permissions.permitted?([:a, :wildcard, :irrelevant])).to be true
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'permits full wildcard' do
|
58
|
-
expect(@permissions.permitted?([:wild, :wildcard, :irrelevant])).to be true
|
59
|
-
expect(@permissions.permitted?([:wild, :irrelevant])).to be true
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'does not permit un-prefixed path' do
|
63
|
-
permissions = Permissions.new(@permissions, :attr)
|
64
|
-
expect(permissions.permitted?(:attr2)).to be false
|
65
|
-
expect(permissions.permitted?([:wild, :irrelevant])).to be false
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'permits with prefix' do
|
69
|
-
permissions = Permissions.new(@permissions, :attr)
|
70
|
-
expect(permissions.permitted?(:array)).to be true
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'does not permit other attributes' do
|
74
|
-
expect(@permissions.permitted?([:attr4, :irrelevant])).to be false
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'does not permit shortened paths' do
|
78
|
-
expect(@permissions.permitted?(:attr)).to be false
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'does not mutate arguments' do
|
82
|
-
attrpaths = [[:attr, :path], :s, [:array, :attr2]]
|
83
|
-
|
84
|
-
@permissions.permitted?(attrpathsarg = attrpaths.dup)
|
85
|
-
expect(attrpathsarg).to eq attrpaths
|
86
|
-
|
87
|
-
prefix = [:prefix]
|
88
|
-
@permissions.permitted?(attrpathsarg = attrpaths.dup)
|
89
|
-
expect(attrpathsarg).to eq attrpaths
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'is indifferent to strings' do
|
93
|
-
expect(@permissions.permitted?(["attr","array"])).to be true
|
94
|
-
expect(@permissions.permitted?("a/wildcard/irrelevant".split("/"))).to be true
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'does not permit single wildcard if tainted' do
|
98
|
-
expect(@permissions.permitted?([:a, :wildcard, "irrelevant".taint])).to be false
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'does not permit full wildcard if tainted' do
|
102
|
-
expect(@permissions.permitted?(["wild".taint, :wildcard, :irrelevant])).to be false
|
103
|
-
expect(@permissions.permitted?(["wild", "irrelevant"].taint)).to be false
|
104
|
-
expect(@permissions.permitted?("a/wildcard/irrelevant".taint.split("/"))).to be false
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
context 'with permit all' do
|
109
|
-
it 'all methods are true' do
|
110
|
-
permissions = Permissions.new.permit_all!
|
111
|
-
expect(permissions.permitted?(:a)).to be true
|
112
|
-
expect(permissions.permitted?(:b)).to be true
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'association methods are false' do
|
116
|
-
permissions = Permissions.new.permit_all!
|
117
|
-
expect(permissions.permitted?([:a,:a])).to be false
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
describe "#select_permitted" do
|
122
|
-
context 'with some attributes permitted' do
|
123
|
-
before(:all) do
|
124
|
-
@permissions = Permissions.new.permit([:another, :array], [:attr, :array, :*], :attr2, :attr3)
|
125
|
-
@permissions.permit([], [:attr, :arrays], :attr2, :attr3)
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'can select something which is permitted' do
|
129
|
-
expect(@permissions.select_permitted(:attr2)).to eq [:attr2]
|
130
|
-
expect(@permissions.select_permitted([:attr, :array, :irrelevant])).to eq [[:attr, :array, :irrelevant]]
|
131
|
-
expect(@permissions.select_permitted([:another, :array])).to eq [[:another, :array]]
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'does not select wildcard path without wildcard' do
|
135
|
-
expect(@permissions.select_permitted([:attr, :array])).to be_empty
|
136
|
-
end
|
137
|
-
|
138
|
-
it 'can select multiple permitted attributes' do
|
139
|
-
attribute_paths = [[:attr2], :attr3, [:another, :array], [:attr, :array, :method]]
|
140
|
-
permitted = @permissions.select_permitted(*attribute_paths)
|
141
|
-
attribute_paths.each do |attribute_path|
|
142
|
-
expect(permitted).to include attribute_path
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'selects only permitted attributes in the original order' do
|
147
|
-
permitted_paths = [:attr2, :attr3, [:attr, :arrays], [:attr, :array, :meth], [:attr3]]
|
148
|
-
unpermitted_paths = [:attr4, :attr5, [:attrk, :irrelevant], [:attr, :ar], [:attr, :arrays, :more]]
|
149
|
-
attribute_paths = (unpermitted_paths + permitted_paths).shuffle
|
150
|
-
expect(@permissions.select_permitted(*attribute_paths)).to eq((attribute_paths & permitted_paths))
|
151
|
-
end
|
152
|
-
|
153
|
-
it 'does not mutate arguments' do
|
154
|
-
attrpaths = [[:attr, :path], :s, [:array, :attr2]]
|
155
|
-
|
156
|
-
@permissions.select_permitted(attrpathsarg = attrpaths.dup)
|
157
|
-
expect(attrpathsarg).to eq attrpaths
|
158
|
-
|
159
|
-
attrpaths = [[:attr, :array, :meth], :s, [:array, :attr2]]
|
160
|
-
@permissions.select_permitted(attrpathsarg = attrpaths.dup)
|
161
|
-
expect(attrpathsarg).to eq attrpaths
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
context 'with permit all' do
|
166
|
-
it 'everything is selected' do
|
167
|
-
permissions = Permissions.new.permit_all!
|
168
|
-
attribute_paths = [:attr2, :attr3, :attr2, :attr, :array]
|
169
|
-
permitted = permissions.select_permitted([:prefix, :array], *attribute_paths)
|
170
|
-
expect(permitted).to eq(attribute_paths)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
describe "#reject_permitted" do
|
175
|
-
context 'with some attributes permitted' do
|
176
|
-
before(:all) do
|
177
|
-
@permissions = Permissions.new.permit([:another, :array], [:attr, :array, :*], :attr2, :attr3)
|
178
|
-
@permissions.permit([], [:attr, :arrays], :attr2, :attr3)
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'selects only unpermitted attributes in the original order' do
|
182
|
-
permitted_paths = [:attr2, :attr3, [:attr, :arrays], [:attr, :array, :meth], [:attr3]]
|
183
|
-
unpermitted_paths = [:attr4, :attr5, [:attrk, :irrelevant], [:attr, :ar], [:attr, :arrays, :more]]
|
184
|
-
attribute_paths = (unpermitted_paths + permitted_paths).shuffle
|
185
|
-
expect(@permissions.reject_permitted(*attribute_paths)).to eq((attribute_paths & unpermitted_paths))
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'does not mutate arguments' do
|
189
|
-
attrpaths = [[:attr, :path], :s, [:array, :attr2]]
|
190
|
-
|
191
|
-
@permissions.reject_permitted(attrpathsarg = attrpaths.dup)
|
192
|
-
expect(attrpathsarg).to eq attrpaths
|
193
|
-
|
194
|
-
attrpaths = [[:attr, :array, :meth], :s, [:array, :attr2]]
|
195
|
-
@permissions.reject_permitted(attrpathsarg = attrpaths.dup)
|
196
|
-
expect(attrpathsarg).to eq attrpaths
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|