attribute_ext 1.1.0 → 1.2.4

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/README.md CHANGED
@@ -58,6 +58,44 @@ or
58
58
  class User < ActiveRecord::Base
59
59
  safe_attributes :login, :if => Proc.new { |user,role| role == :admin }
60
60
  end
61
+
62
+ Default role and role mapper:
63
+
64
+ SafeAttributes provides helper for handling roles including a method to set
65
+ a new default role as well as a method to map roles to other values. Changes to
66
+ role will only affect SafeAttributes and will not be given to Rails 3.1 mass
67
+ assignment authorizer.
68
+
69
+ Set default role that will be used if given role is nil or :default.
70
+
71
+ AttributeExt::SafeAttributes.default_role = :new_default
72
+
73
+ Role values can be restricted to specific values using the role mapper.
74
+
75
+ AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
76
+ [:guest, :user, :admin].include?(role) ? role : :guest
77
+ end
78
+
79
+ or
80
+
81
+ AttributeExt::SafeAttributes.role_mapper do |role|
82
+ [:guest, :user, :admin].include?(role) ? role : :guest
83
+ end
84
+
85
+ The role mapper is especially usefull if you want the current user model be the
86
+ default role.
87
+
88
+ AttributeExt::SafeAttributes.role_mapper do |role|
89
+ role.is_a?(User) ? role : User.current
90
+ end
91
+
92
+ You can perform checks like this now:
93
+
94
+ class User < ActiveRecord::Base
95
+ safe_attribute :email, :if => Proc.new { |user,role| user == role or role.admin? }
96
+ end
97
+
98
+ Now the user can edit there own emails or everyons email if it is an admin.
61
99
 
62
100
 
63
101
  AttributeExt::HiddenAttributes
@@ -115,6 +153,12 @@ By default rules *do not* apply when serializing to hash.
115
153
  Changelog
116
154
  ---------
117
155
 
156
+ Sep 24, 2011
157
+
158
+ SafeAttributes provides methods to change default role and to map roles to
159
+ specific values before processing rules. Also added full documentation to
160
+ all public methods and methods that are usefull for testing own rules.
161
+
118
162
  Sep 22, 2011
119
163
 
120
164
  Nearly all features are successfully tested using a fake environment now.
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "attribute_ext"
6
- s.version = "1.1.0"
6
+ s.version = "1.2.4"
7
7
  s.authors = ["Jan Graichen"]
8
8
  s.email = ["jan.graichen@altimos.de"]
9
9
  s.homepage = "https://github.com/jgraichen/attribute_ext"
data/init.rb CHANGED
@@ -1,5 +1,4 @@
1
1
 
2
2
  require 'attribute_ext'
3
3
 
4
- ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
5
- ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
4
+ AttributeExt.setup
@@ -2,3 +2,10 @@
2
2
  require 'attribute_ext/hidden_attributes'
3
3
  require 'attribute_ext/safe_attributes'
4
4
  require 'attribute_ext/railtie' if defined?(Rails)
5
+
6
+ module AttributeExt
7
+ def AttributeExt.setup # :nodoc:
8
+ ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
9
+ ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
10
+ end
11
+ end
@@ -1,6 +1,6 @@
1
1
  module AttributeExt
2
2
  module HiddenAttributes
3
- def self.included(base)
3
+ def self.included(base) # :nodoc:
4
4
  base.extend(ClassMethods)
5
5
  base.alias_method_chain :to_xml, :hidden_attrs
6
6
  base.alias_method_chain :as_json, :hidden_attrs
@@ -8,6 +8,39 @@ module AttributeExt
8
8
  end
9
9
 
10
10
  module ClassMethods
11
+ # Adds attribute to a blacklist that will be hidden when serializing if optional conditions
12
+ # are true.
13
+ #
14
+ # class User < ActiveRecord::Base
15
+ # hide_attributes :password # always hide
16
+ # hide_attributes :email, :if => Proc.new { |user| user.hide_email? }
17
+ # hide_attributes :not_in_json, :only => :json
18
+ # hide_attributes :except_xml_hash, :except => [:xml, :hash]
19
+ # end
20
+ #
21
+ # All given conditions to a rule must be true if attributes should be hidden. Attributes can
22
+ # appear in more than one rule.
23
+ #
24
+ # Options:
25
+ # [:+if+]
26
+ # Requires a Proc block to be true.
27
+ #
28
+ # [:+unless+]
29
+ # Requires a Proc block to be false.
30
+ #
31
+ # [:+only+]
32
+ # Requires export format to be in given array. A non array object will be converted in to
33
+ # an array only containing given object.
34
+ #
35
+ # [:+except+]
36
+ # Requires export format to not be in given array. A non array object will be converted in
37
+ # to an array only containing given object.
38
+ #
39
+ # [:+on_hash+]
40
+ # By default rules will not be applied when serializing to hash when no :only or :except
41
+ # rule is specified. If :on_hash is true rule will also apply to hash serialization. If an
42
+ # :only or :except option is given :on_hash does nothing.
43
+ #
11
44
  def hide_attributes(*attrs)
12
45
  @hidden_attributes ||= []
13
46
  if attrs.empty?
@@ -34,14 +67,14 @@ module AttributeExt
34
67
  end
35
68
  end
36
69
 
37
- def to_xml_with_hidden_attrs(options = nil, &block)
70
+ def to_xml_with_hidden_attrs(options = nil, &block) # :nodoc:
38
71
  options ||= {}
39
72
  options[:except] = hidden_attribute_names(:xml, options)
40
73
 
41
74
  to_xml_without_hidden_attrs(options)
42
75
  end
43
76
 
44
- def as_json_with_hidden_attrs(options = nil, &block)
77
+ def as_json_with_hidden_attrs(options = nil, &block) # :nodoc:
45
78
  options ||= {}
46
79
  options[:except] = hidden_attribute_names(:json, options)
47
80
  options[:hidden_attributes_format] = :json
@@ -49,13 +82,20 @@ module AttributeExt
49
82
  as_json_without_hidden_attrs(options)
50
83
  end
51
84
 
52
- def serializable_hash_with_hidden_attrs(options = nil)
85
+ def serializable_hash_with_hidden_attrs(options = nil) # :nodoc:
53
86
  options ||= {}
54
87
  options[:except] = hidden_attribute_names((options[:hidden_attributes_format] || :hash), options)
55
88
 
56
89
  serializable_hash_without_hidden_attrs(options)
57
90
  end
58
91
 
92
+ # Returns an array with attributes to hide from serialization.
93
+ #
94
+ # This method should only be used to test own rules without need to run a formatter and
95
+ # validate the generated output. See AttributeExt specs for details.
96
+ #
97
+ # hidden_attribute_names :format, :options => :hash
98
+ #
59
99
  def hidden_attribute_names(format, options = {})
60
100
  if options[:except].is_a?(Array)
61
101
  names = options[:except]
@@ -1,10 +1,9 @@
1
1
 
2
2
  module AttributeExt
3
- class Railtie < Rails::Railtie
3
+ class Railtie < Rails::Railtie # :nodoc:
4
4
  initializer 'attribute_ext' do |app|
5
5
  ActiveSupport.on_load :active_record do
6
- ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
7
- ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
6
+ AttributeExt.setup
8
7
  end
9
8
  end
10
9
  end
@@ -1,11 +1,81 @@
1
1
  module AttributeExt
2
2
  module SafeAttributes
3
- def self.included(base)
3
+ # Returns default role used by SafeAttributes.
4
+ # See SafeAttributes#default_role= for how to specify a default role.
5
+ def SafeAttributes.default_role
6
+ @default_role || :default
7
+ end
8
+
9
+ # Sets SafeAttributes default role that will be used when given role
10
+ # is a nil value or the :default role. The SafeAttributes default role will
11
+ # only affect this extension and will not be given to Rails 3.1 mass
12
+ # assignment authorizer.
13
+ def SafeAttributes.default_role=(role)
14
+ @default_role = role
15
+ end
16
+
17
+ # Returns current role mapper block or sets role mapper if an block is
18
+ # given. By default no role mapper is active.
19
+ #
20
+ # AttributeExt::SafeAttributes.role_mapper do |role|
21
+ # [:guest, :user, :admin].include?(role) ? role : :guest
22
+ # end
23
+ #
24
+ def SafeAttributes.role_mapper(&block)
25
+ self.role_mapper = block if block
26
+ @role_mapper
27
+ end
28
+
29
+ # Sets current role mapper to given Proc or removes role mapper if
30
+ # a nil value is given. Any other value will do nothing.
31
+ #
32
+ # AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
33
+ # [:guest, :user, :admin].include?(role) ? role : :guest
34
+ # end
35
+ #
36
+ # See SafeAttributes#role_mapper for an short way to set a role mapper.
37
+ def SafeAttributes.role_mapper=(role_mapper)
38
+ @role_mapper = role_mapper if role_mapper.is_a?(Proc)
39
+ @role_mapper = nil if role_mapper.nil?
40
+ end
41
+
42
+ def self.included(base) # :nodoc:
4
43
  base.extend(ClassMethods)
5
44
  base.alias_method_chain :mass_assignment_authorizer, :safe_attrs
6
45
  end
7
46
 
47
+
8
48
  module ClassMethods
49
+ # Adds a whitelist rule that allows mass assignment for given attributes
50
+ # based on given optional conditions.
51
+ #
52
+ # class User < ActiveRecord::Base
53
+ # # always mass assignable
54
+ # safe_attributes :name, :email
55
+ # # only when new record
56
+ # safe_attributes :login, :if => Proc.new { |user| user.new_record? }
57
+ # # only own password or as admin
58
+ # safe_attributes :password, :if => Proc.new { |user,role| user == role }
59
+ # safe_attributes :password, :as => :admin
60
+ # end
61
+ #
62
+ # All given conditions for one rule must be true to allow mass
63
+ # assignment for given attributes. Attributes can be added in more than
64
+ # one rule to allow alternatives (like password above).
65
+ #
66
+ # Available Options:
67
+ # [:+as+]
68
+ # Attributes will be assignable if mass assignment role is equal (==) given object.
69
+ #
70
+ # [:+if+]
71
+ # Makes attributes assignable if given Proc block returns true.
72
+ #
73
+ # [:+unless+]
74
+ # Attributes cannot be mass assigned if Proc block evaluates to true.
75
+ #
76
+ # The :if and :unless options must be Proc block that will be executed each time the
77
+ # mass assignment authorizer is called and they are called with current
78
+ # model and role as parameters.
9
79
  def safe_attributes(*attrs)
10
80
  @safe_attributes ||= []
11
81
  if attrs.empty?
@@ -26,12 +96,34 @@ module AttributeExt
26
96
  end
27
97
  end
28
98
 
29
- def mass_assignment_authorizer_with_safe_attrs(role = nil)
30
- attrs = role.nil? ? mass_assignment_authorizer_without_safe_attrs : mass_assignment_authorizer_without_safe_attrs(role)
31
- attrs += safe_attribute_names(role ? role : :default)
99
+ def mass_assignment_authorizer_with_safe_attrs(role = nil) # :nodoc:
100
+ if role.nil?
101
+ attrs = mass_assignment_authorizer_without_safe_attrs +
102
+ safe_attribute_names
103
+ else
104
+ attrs = mass_assignment_authorizer_without_safe_attrs(role) +
105
+ safe_attribute_names(role)
106
+ end
107
+ end
108
+
109
+ # Returns new mapped role for given role used by SafeAttributes.
110
+ # This method should only be used to test own role mapper implementations without need for a
111
+ # full application. See AttributeExt specs for details.
112
+ #
113
+ # See +role_mapper+ method in SafeAttributes module for how to set a role mapper.
114
+ def safe_attributes_role(role = nil)
115
+ return AttributeExt::SafeAttributes.role_mapper.call(role) unless AttributeExt::SafeAttributes.role_mapper.nil?
116
+ return AttributeExt::SafeAttributes.default_role if role.nil? or role == :default
117
+ role
32
118
  end
33
119
 
34
- def safe_attribute_names(role = :default)
120
+ # Returns an array with attributes allowed to be mass assigned by given role. Role will be
121
+ # mapped before given to rules.
122
+ # This method should only be used to test own rules without need to create lots of records
123
+ # to test different situations. See AttributeExt specs for details.
124
+ def safe_attribute_names(role = nil)
125
+ role = safe_attributes_role(role)
126
+
35
127
  names = []
36
128
  self.class.safe_attributes.collect do |attrs, options|
37
129
  next unless options[:as].empty? or options[:as].include?(role)
@@ -62,4 +62,61 @@ describe AttributeExt::HiddenAttributes do
62
62
  user = User.new
63
63
  user.mass_assignment_authorizer(:admin).should_not include('attribute_unless_admin')
64
64
  end
65
+
66
+ it 'can provide a global default role' do
67
+ AttributeExt::SafeAttributes.default_role = :new_default
68
+ AttributeExt::SafeAttributes.default_role.should == :new_default
69
+ User.new.mass_assignment_authorizer.should include("new_default")
70
+ end
71
+
72
+ context '#role_mapper' do
73
+ it 'is nil by default' do
74
+ AttributeExt::SafeAttributes.role_mapper.should be_nil
75
+ end
76
+
77
+ it 'accept Procs' do
78
+ proc = Proc.new { |role| role }
79
+ AttributeExt::SafeAttributes.role_mapper = proc
80
+ AttributeExt::SafeAttributes.role_mapper.should equal(proc)
81
+ end
82
+
83
+ it 'accept nil' do
84
+ AttributeExt::SafeAttributes.role_mapper = nil
85
+ AttributeExt::SafeAttributes.role_mapper.should be_nil
86
+ end
87
+
88
+ it 'accept blocks' do
89
+ AttributeExt::SafeAttributes.role_mapper { |role| role }
90
+ AttributeExt::SafeAttributes.role_mapper.should be_an Proc
91
+ end
92
+
93
+ it 'maps role according to given Proc' do
94
+ AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
95
+ [:guest, :user, :admin].include?(role) ? role : :guest
96
+ end
97
+
98
+ User.new.safe_attributes_role.should == :guest
99
+ User.new.safe_attributes_role(:user).should == :user
100
+ User.new.safe_attributes_role(:admin).should == :admin
101
+ User.new.safe_attributes_role(:heinz).should == :guest
102
+ end
103
+
104
+ it 'gives role to rules' do
105
+ AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
106
+ [:guest, :user, :admin].include?(role) ? role : :guest
107
+ end
108
+
109
+ User.new.mass_assignment_authorizer.should include('role_mapper_guest')
110
+ User.new.mass_assignment_authorizer.should_not include('role_mapper_user')
111
+ User.new.mass_assignment_authorizer.should_not include('role_mapper_admin')
112
+
113
+ User.new.mass_assignment_authorizer(:user).should_not include('role_mapper_guest')
114
+ User.new.mass_assignment_authorizer(:user).should include('role_mapper_user')
115
+ User.new.mass_assignment_authorizer(:user).should_not include('role_mapper_admin')
116
+
117
+ User.new.mass_assignment_authorizer(:admin).should_not include('role_mapper_guest')
118
+ User.new.mass_assignment_authorizer(:admin).should_not include('role_mapper_user')
119
+ User.new.mass_assignment_authorizer(:admin).should include('role_mapper_admin')
120
+ end
121
+ end
65
122
  end
@@ -54,6 +54,10 @@ class User < ActiveRecord::Base
54
54
  :if => Proc.new { |user,role| role == :admin}
55
55
  safe_attributes :attribute_unless_admin,
56
56
  :unless => Proc.new { |user,role| role == :admin}
57
+ safe_attributes :new_default, :as => :new_default
58
+ safe_attributes :role_mapper_guest, :if => Proc.new { |user,role| role == :guest }
59
+ safe_attributes :role_mapper_user, :as => :user
60
+ safe_attributes :role_mapper_admin, :if => Proc.new { |user,role| role == :admin }
57
61
 
58
62
  def initialize(opts = {})
59
63
  @opts = {
@@ -61,4 +65,4 @@ class User < ActiveRecord::Base
61
65
  :unless => true
62
66
  }.merge(opts.is_a?(Hash) ? opts : {})
63
67
  end
64
- end
68
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attribute_ext
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 1
9
- - 0
10
- version: 1.1.0
8
+ - 2
9
+ - 4
10
+ version: 1.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jan Graichen
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-09-22 00:00:00 Z
18
+ date: 2011-09-24 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: AttributeExt provides additional access control for rails model attributes.