strong_presenter 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # StrongPresenter Changelog
2
2
 
3
+ ## 0.2.2
4
+ - remove arguments from permission checking - complex and potentially very inefficient
5
+ - extract permissions into separate gem
6
+
3
7
  ## 0.2.1
4
8
 
5
9
  - Bug fix: `presents` with nested associations
@@ -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 StrongPresenter::Permissible
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
@@ -1,3 +1,3 @@
1
1
  module StrongPresenter
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -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! :*
@@ -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.1
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-10-25 00:00:00.000000000 Z
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: '3.0'
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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
- metadata: {}
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: 2.0.3
329
+ rubygems_version: 1.8.17
359
330
  signing_key:
360
- specification_version: 4
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
-