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 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
-